Lexa

WPF, C#, Objective C and a little Math

AM: Waiting for Review

Well, meanwhile Arcade Monsterball was uploaded and is waiting for review. That was quite a hard task to complete, but it finally over.

3717847-00087

Here is my completed TODO list (what a nightmare to complete):

+ Make screenshots
+ (iOS) check for leaks
+ (iOS) iPhoneLayout
+ background of menus: flying shadows of spaceships
+ sync player as well in network game (when sending hit information)
+ (iOS) handle pause events in iOS
+ Points shall be assigned even if lost
+ (iOS) enlarge touch area to whole screen
+ (iOS) invitation of a player
+ (iOS) achivements for keeping locked players
+ bugfix – achievment and point number at the same time
+ achievements icons
+ (iOS) leaderbords: points overall and points in the single match
+ (iOS) shift upper UI elements if iAd is dsplayed
+ points of network player shall come from network player (?)
+ use bitmap fonts instead of TTF
+ help screen content
+ game win menu: add collected points submenu + unlocked players menu.
+ save selected player and position into the file
+ when creating stars: with very small probability a planet should be created (probably rotating)
+ move shadow to separate class
+ play network with AI
+ help screen:
+ should show 2-3 screens with instructions (made from single texture)
+ cleanup texture cache
+ all balls shall use the same texture
+ load different balls each serve
+ skin ball
+ different ball types
+ game data management (available players, points, etc.)
+ multiply achivement of the game on number of hits
+ skin menus in GameLayer
+ fire on enemy select screen
+ indcator for ball hits in upper region
+ player type shall be sent for network game
+ (iOS) iad advertisement
+ two fires (jump and normal) for firing monsters
+ add music to runtime
+ select enemy screen
+ different player models
+ icon
+ MainMenu UI for iPhone shall be ~20% larger than usuall scale (0.3125 -> 0.4, 0.2 for non-HD version)
+ atmosphere for the moon
+ fix problem with “Return from the othr side”
+ sky texture shall have 100 px width
+ particle systems: dust and fire
+ animated background – asteroids, space ships, asteroids collisions, Earth, etc.
+ remove bouncing around a point (see TODO in PositionPlayerControl)
+ skin for iphone/ipad “control pad”
+ finish touch player control
+ render shadows using physix (renderer) body information
+ shake ground when ball hits it
+ (iOS) vertical alignment on iPhone and iPod
+ particles
+ stars and rockets on the background
+ stars moving altogether
+ fix problems with resolution
+ Shadow of the players and the ball
+ change controls (use mouse joint)
+ use client – server architecture (including test environement)
+ Load level from json
+ Load players and a ball from json
+ introduce game state
+ the ball shall be alive a little after the score
+ the ball shall stay in air until the first touch
+ keyboard for windows
+ named walls
+ ai
++ basic ai
++ advanced ai
++ best position calculation is done not at once, but certain amount of tests each update.
++ parametrized ai
++ ai shall get tired -> more stupid after with time
+ sound
+ game menu
+ animated players (move_forward, move_backward, jump)
+ match
+ ESCAPE button for windows version
+ network game for win version
+ keyborad fix: only process if current window is active
+ pause screen during network game
+ network game timeout (no messages during N ticks) – “Player disconnected”
+ network game/single game – show current player
+ game ui: pause shall be a 2 buttons names “pause” – left and right to score
+ game ui: icons in the corner: type of the player (local, remote, AI)
+ game ui: ball picture for serving
+ game ui: hit count (for tests)
+ game ui: points
+ points for: game win (x rest hit counts), single touch (x2, x3, etc.), back wall hit (x2, x3, etc.), 2 games in a row (x3, x4, etc.)
+ menu structure design
+ points for “flawless victory”

During the project I learned quite a lot about cocos2d, box2d, R.U.B.E. and inkscape. The other fancy tool I discovered is an online bitmap font generator: http://kvazars.com/littera/. Brilliant thing.


cocos2dx addSpriteFramesWithFile problem – garbage on the screen

I noticed that in some cases after addSpriteFramesWithFile function is called, the loaded frames are displayed for 1 frame. This was is quite disturbing and exists both in Windows implemenation and on iOS devices.

I do not really know what is the reason for this (might be details of addSpriteFramesWithFile implementation), but the solution was simple: after addSpriteFramesWithFile is done, I call visit() of the current scene, and nothing unusual appears on the screen.


cocos2dx – ccDrawCircle, solid, filled?

I tried to draw a solid cicle in cocos2dx. It was quite terrible to modify ccDrawCircle for this. My app crashed with openGL errors which I was not able to handle. Few days later (after I left this idea and used spites instead) I realized that I should use ccDrawSolidPoly. What a shame :)

Here is the code anyway:

void ccDrawSolidCircle(const CCPoint& center, float radius, float angle, unsigned int segments, float scaleX, float scaleY, ccColor4F color) 
{
	// Copypaste from ccDrawCircle to build poli buffer.	
    const float coef = 2.0f * (float)M_PI/segments;

    CCPoint *poli = (CCPoint*)calloc(sizeof(CCPoint)*(segments+1), 1);
    if (!poli)
        return;

    for(unsigned int i = 0;i <= segments; i++) {
        float rads = i*coef;
	poli[i].x = radius * cosf(rads + angle) * scaleX + center.x;
        poli[i].y = radius * sinf(rads + angle) * scaleY + center.y;
    }
    ccDrawSolidPoly(poli, segments + 1, color);
}

Meanwhile, Arcade Monsterball grew up to quite colorful game. Still a lot TODOs, but major things are done:


Arcade Monsterball

Manwhile I am making a small game – a remake of the famous Arcade Volleyball.

It is portable  – I use cocos2d-x, so iOS and at least Windows versions will be available. Here is Windows demo. Network gaming is supported as well ;)

What is alse used:

  • Box2D
  • ENet for networking in Windows
  • R.U.B.E.
  • TexturePacker

iOS 7

Recently I installed a new version of the iOS. First impression could be probably described as “what the hell is this”. But quite soon I get used to it. It is definitely a step forward, it is modern, it has good new ideas. UI is also a bit buggy, but this is totally OK for a beta release. Nasty thing is that a man has to do quite some job to make an app (especially an app which uses custom stuff like icons, buttons or gradients) look OK for the iOS 7.


Box2D DebugDraw (b2Draw implementation) for WINAPI, WINGDI

Quite often I create simple or test apps using native Win32 project (WINAPI). This time I wanted an output of Box2D world. No ready-to-copypaste implementation was found in the Net, so I had to make my own, and now I can share it :)

And here is the source code:

Header file

// DebugDrawGDI.h
// 
// 
// Created by PavlAl on 06.05.2013
// Copyright (c) 2013 tatalata, http://talatala.com
// 

#include <Box2D/Box2D.h>

#pragma once

class DebugDrawGDI : public b2Draw
{
public:
	DebugDrawGDI();
	~DebugDrawGDI();

	/// Initialize drawing of a whole world.
	void Begin(RECT & winRect, HDC hdc, b2World *world);

	/// Initialize drwaing in a certain AABB.
	void Begin(RECT & winRect, HDC hdc, b2World *world, const b2AABB & aabb);

	/// Unintialize drawing.
	void End();

	/// Draw a closed polygon provided in CCW order.
	virtual void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color);

	/// Draw a solid closed polygon provided in CCW order.
	virtual void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color);

	/// Draw a circle.
	virtual void DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color);

	/// Draw a solid circle.
	virtual void DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color);

	/// Draw a line segment.
	virtual void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color);

	/// Draw a transform. Choose your own length scale.
	/// @param xf a transform.
	virtual void DrawTransform(const b2Transform& xf);

private:
	/// Setup current brush and pen.
	void SetColor(const b2Color& color);
	/// Delete current brush and pen.
	void CleanupGDIObjects();

private:
	/// Reference to a HDC set in Begin method. 
	HDC m_hdc;
	/// Last used brush, owned by DebugDrawGDI.
	HBRUSH m_brush;
	/// Last used pen, DebugDrawGDI.
	HPEN m_pen;
	/// Last used color.
	DWORD m_brushColor;
};

Implementation file

// DebugDrawGDI.cpp
// 
// 
// Created by PavlAl on 06.05.2013
// Copyright (c) 2013 tatalata, http://talatala.com
// 
#include "stdafx.h"
#include "DebugDrawGDI.h"
#include <vector>

#define DEBUG_DRAW_MAX_WORLD_SIZE 1000
#define SCALE 1000.f
#define UP(v) ((v) * SCALE)
#define UPi(v) ((int)UP(v))

class AABBDetector : public b2QueryCallback
{
public:
	b2AABB aabb;

	AABBDetector()
	{
		aabb.lowerBound = b2Vec2(DEBUG_DRAW_MAX_WORLD_SIZE, DEBUG_DRAW_MAX_WORLD_SIZE);
		aabb.upperBound = b2Vec2(-DEBUG_DRAW_MAX_WORLD_SIZE, -DEBUG_DRAW_MAX_WORLD_SIZE);
	}

	bool ReportFixture(b2Fixture* fixture)
	{
		b2Vec2 pos = fixture->GetBody()->GetPosition();

		if (aabb.lowerBound.x > pos.x) aabb.lowerBound.x = pos.x;
		if (aabb.upperBound.x < pos.x) aabb.upperBound.x = pos.x;
		if (aabb.lowerBound.y > pos.y) aabb.lowerBound.y = pos.y;
		if (aabb.upperBound.y < pos.y) aabb.upperBound.y = pos.y;

		return true;
	}
};

namespace
{
	b2AABB CreateAABB(float xMin, float yMin, float xMax, float yMax)
	{
		b2AABB aabb;
		aabb.lowerBound = b2Vec2(xMin, yMin);
		aabb.upperBound = b2Vec2(xMax, yMax);
		return aabb;
	}
}

DebugDrawGDI::DebugDrawGDI()
: m_hdc(NULL)
, m_brush(NULL)
, m_pen(NULL)
, m_brushColor(0)
{
}

DebugDrawGDI::~DebugDrawGDI()
{
	CleanupGDIObjects();
}

void DebugDrawGDI::Begin(RECT & winRect, HDC hdc, b2World *world, const b2AABB & aabb)
{
	m_hdc = hdc;
	world->SetDebugDraw(this);

	BOOL bOK = SetGraphicsMode(hdc, GM_ADVANCED);
	ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);

	// Cleanup area.
	FillRect(hdc, &winRect, (HBRUSH)GetStockObject(WHITE_BRUSH));

	// Change world transform.
	int outputWidth = winRect.right - winRect.left;
	int outputHeight = winRect.bottom - winRect.top;
	float boundsWidth = UP(aabb.upperBound.x - aabb.lowerBound.x);
	float boundsHeight = UP(aabb.upperBound.y - aabb.lowerBound.y);
	float cx = outputWidth / boundsWidth;
	float cy = outputHeight / boundsHeight;
	float c = cx > cy ? cy : cx;

	XFORM x;
	x.eM11 = c;
	x.eM21 = 0;
	x.eDx = winRect.left + (outputWidth - boundsWidth * c) / 2 - UP(aabb.lowerBound.x) * c;
	x.eM12 = 0;
	x.eM22 = -c;
	x.eDy = winRect.top + (outputHeight - boundsHeight * c) / 2 + UP(aabb.upperBound.y) * c;

	SetWorldTransform(hdc, &x);
}

void DebugDrawGDI::Begin(RECT & winRect, HDC hdc, b2World *world)
{
	// Get world AABB.
	AABBDetector d;
	world->QueryAABB(&d, CreateAABB(-DEBUG_DRAW_MAX_WORLD_SIZE, -DEBUG_DRAW_MAX_WORLD_SIZE, DEBUG_DRAW_MAX_WORLD_SIZE, DEBUG_DRAW_MAX_WORLD_SIZE));

	d.aabb.lowerBound -= b2Vec2(1, 1);
	d.aabb.upperBound += b2Vec2(1, 1);

	Begin(winRect, hdc, world, d.aabb);
}

void DebugDrawGDI::End()
{
	ModifyWorldTransform(m_hdc, NULL, MWT_IDENTITY);
	m_hdc = NULL;
}

void DebugDrawGDI::CleanupGDIObjects()
{
	if (m_brush) ::DeleteObject(m_brush);
	if (m_pen) ::DeleteObject(m_pen);

	m_brush = NULL;
	m_pen = NULL;
}

void DebugDrawGDI::SetColor(const b2Color& color)
{
	DWORD rgb = RGB(color.r * 255, color.g*255, color.b*255);
	if (!m_brush || !m_pen || m_brushColor != rgb)
	{
		CleanupGDIObjects();

		m_brush = ::CreateSolidBrush(rgb);
		m_pen = ::CreatePen(PS_SOLID, 1, RGB(color.r * 155, color.g*155, color.b*155));
		m_brushColor = rgb;
	}

	::SelectObject(m_hdc, m_brush);
	::SelectObject(m_hdc, m_pen);
}

/// Draw a closed polygon provided in CCW order.
void DebugDrawGDI::DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color)
{
    static std::vector<POINT> pts;
    pts.resize(vertexCount + 1);
    for (int i = 0; i < vertexCount; ++i, ++vertices)
    {
        POINT p;
        p.x = UPi(vertices->x);
        p.y = UPi(vertices->y);
        pts[i] = p;
    }
    pts[vertexCount] = pts.front();
    
    SetColor(color);
    ::Polyline(m_hdc, &pts[0], vertexCount + 1);
}

/// Draw a solid closed polygon provided in CCW order.
void DebugDrawGDI::DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color)
{
    static std::vector<POINT> pts;
    pts.resize(vertexCount + 1);
    for (int i = 0; i < vertexCount; ++i, ++vertices)
    {
        POINT p;
        p.x = UPi(vertices->x);
        p.y = UPi(vertices->y);
        pts[i] = p;
    }
    pts[vertexCount] = pts.front();
    
    SetColor(color);
    ::Polygon(m_hdc, &pts[0], vertexCount + 1);
}

/// Draw a circle.
void DebugDrawGDI::DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color)
{
	SetColor(color);

	// Cleanup fill brush.
	::SelectObject(m_hdc, GetStockObject(NULL_BRUSH));

	::Ellipse(
		m_hdc, 
		UPi(center.x - radius),
		UPi(center.y - radius),
		UPi(center.x + radius),
		UPi(center.y + radius));

	SetColor(color);
}

/// Draw a solid circle.
void DebugDrawGDI::DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color)
{
	SetColor(color);

	::Ellipse(
		m_hdc, 
		UPi(center.x - radius),
		UPi(center.y - radius),
		UPi(center.x + radius),
		UPi(center.y + radius));

	DrawSegment(center, center + b2Vec2(axis.x * radius, axis.y *radius), color);
}

/// Draw a line segment.
void DebugDrawGDI::DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color)
{
	SetColor(color);

	::MoveToEx(m_hdc, UPi(p1.x), UPi(p1.y), NULL);
	::LineTo(m_hdc, UPi(p2.x), UPi(p2.y));
}

/// Draw a transform. Choose your own length scale.
/// @param xf a transform.
void DebugDrawGDI::DrawTransform(const b2Transform& xf)
{
	const float length = 1;
	b2Vec2 start = xf.p;
	b2Vec2 axis = xf.q.GetXAxis();
	b2Vec2 end = start + b2Vec2(axis.x * length, axis.y * length);
	::MoveToEx(m_hdc, UPi(start.x), UPi(start.y), NULL);
	::LineTo(m_hdc, UPi(end.x), UPi(end.y));
}

The Last Mission v 1.2 released

Wow! A new release is finally available in the AppStore: The Last Mission 1.2

It includes following features:

– 4 new playable ships with different weapons.
– Health bonus in easy level.
– Improved graphics: dynamic lights and details on level backgrounds.
– New music themes and sound effects created by Mark Braga specially for The Last Mission.
– Brand new secret level with bonus ship at the end.
– Integration with the Game Center (worldwide leader board).
– Game status is saved between app runs.
– Updated GUI with a little help on the game.
– Adjusted controls positions for iPad, iCade support (bluetooth joystics).

Now game is finally playable and fun, I am very happy with results and can continue with my other project :)

Meanwhile, the author of the remake, Dmitry Smagin is planning to continue development and release the new version for Windows and open-source portable systems. I’m looking forward for it.