<file_sep>package com.letsrace.game.screen;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.letsrace.game.FRConstants;
import com.letsrace.game.LetsRace;

public class FRWaitScreen extends ScreenAdapter{
	private LetsRace gameRef;
	private final static float ROTATION = 40;
	private final static float DURATION = 0.65f;
	
	public FRWaitScreen(LetsRace letsRace) {
		Gdx.app.log(FRConstants.TAG, "Wait: Constructor");
		gameRef = letsRace;
		gameRef.stage.clear();
		Gdx.input.setInputProcessor(gameRef.stage);
		Image image = new Image(gameRef.skin.getDrawable("background"));
		image.setWidth(Gdx.graphics.getWidth());
		image.setHeight(Gdx.graphics.getHeight());
		image.setZIndex(1);
		gameRef.stage.addActor(image);
		Image steering = new Image(gameRef.skin.getDrawable("wait-screen-steer"));
		steering.setWidth(210 * FRConstants.GUI_SCALE_WIDTH);
		steering.setHeight(210 * FRConstants.GUI_SCALE_WIDTH);
		steering.setPosition(Gdx.graphics.getWidth()/2 - steering.getWidth()/2, Gdx.graphics.getHeight()/2  - steering.getHeight()/2);
		steering.setOrigin(steering.getWidth()/2,steering.getHeight()/2);
		steering.addAction(Actions.forever(Actions.sequence(Actions.rotateBy(ROTATION,DURATION),Actions.rotateBy(-2*ROTATION,DURATION*2),Actions.rotateBy(ROTATION,DURATION),Actions.rotateBy(ROTATION*4,DURATION),Actions.rotateBy(-ROTATION*4,DURATION))));
		gameRef.stage.addActor(steering);		
	}
	public void show(){
		Gdx.app.log(FRConstants.TAG, "Wait: show()");
		// Throwing stuff in constructor as loading stuff in show() causes black screen
	}
	
	@Override
	public void render(float delta) {
		GL20 gl = Gdx.gl;
		gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		gameRef.stage.act(delta);
		gameRef.stage.draw();
	}
	
	public void dispose() {
		Gdx.app.log(FRConstants.TAG, "Wait: dispose()");
	}
	
	

}
<file_sep>package com.letsrace.game.screen;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

import aurelienribon.tweenengine.BaseTween;
import aurelienribon.tweenengine.Tween;
import aurelienribon.tweenengine.TweenCallback;
import aurelienribon.tweenengine.TweenManager;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.input.GestureDetector;
import com.badlogic.gdx.input.GestureDetector.GestureListener;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Timer;
import com.badlogic.gdx.utils.Timer.Task;
import com.letsrace.game.CameraTweenAccessor;
import com.letsrace.game.FRConstants;
import com.letsrace.game.LetsRace;
import com.letsrace.game.Message;
import com.letsrace.game.FRConstants.GameState;
import com.letsrace.game.network.FRMessageCodes;
import com.letsrace.game.unused.FRAssets;

public class FRArenaSelectScreen extends ScreenAdapter implements
		GestureListener {

	LetsRace gameRef;
	SpriteBatch batch;
	Camera camera;
	Vector3 touchPoint;
	ArrayList<String> arenaNames;
	int currentPosition = 1;

	Sprite logo;
	Sprite heading;
	Sprite trackOverview;
	Sprite arrow;
	Sprite background;
	Sprite endHint;

	TweenManager tweenMgr;
	GestureDetector gesture;

	float camHalfWidth;
	float camHalfHeight;

	enum State {
		WAITING_FOR_SERVER, IS_SERVER_SELECT_ARENA
	}

	private boolean isAnimating = false;
	private boolean showLeft = false, showRight = false;
	private State screenState;

	public FRArenaSelectScreen(LetsRace letsRace) {
		Gdx.app.log(FRConstants.TAG, "ArenaSelect: Constructor");
		gameRef = letsRace;
		gameRef.stage.clear();
		batch = new SpriteBatch();
		camera = new OrthographicCamera(6, 10);
		camHalfWidth = camera.viewportWidth / 2f;
		camHalfHeight = camera.viewportHeight / 2f;
		camera.position.set(currentPosition * camHalfWidth, camHalfHeight, 0);
		camera.update();
		batch.setProjectionMatrix(camera.combined);
		touchPoint = new Vector3();
		if (gameRef.isServer()) {
			screenState = State.IS_SERVER_SELECT_ARENA;
			tweenMgr = new TweenManager();
			gesture = new GestureDetector(this);
			Gdx.input.setInputProcessor(gesture);
			Tween.registerAccessor(Camera.class, new CameraTweenAccessor());
			loadNames();
		} else {
			screenState = State.WAITING_FOR_SERVER;
		}
	}

	private void loadNames() {
		arenaNames = new ArrayList<String>();
		FileHandle handle = Gdx.files.internal("arenaNames.txt");
		if (handle.exists()) {
			BufferedReader input = new BufferedReader(new InputStreamReader(
					handle.read()));
			String name;
			try {
				while ((name = input.readLine()) != null) {
					arenaNames.add(name);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void render(float delta) {
		GL20 gl = Gdx.gl;
		gl.glClearColor(0.8f, 0.8f, 0.8f, 1);
		gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

		switch (screenState) {
		case IS_SERVER_SELECT_ARENA:
			updateSelect(delta);
			renderSelect(delta);
			break;
		case WAITING_FOR_SERVER:
			updateWaiting(delta);
			renderWaiting(delta);
			break;
		}
	}

	private void renderWaiting(float delta) {
		batch.begin();
		background = FRAssets.background;
		background.setSize(camHalfWidth * 2, camHalfHeight * 2);
		background.setPosition(0, 0);
		background.draw(batch);
		batch.end();
	}

	private void updateWaiting(float delta) {
		Message msg;
		while ((msg = gameRef.network.readFromClientQueue()) != null) {
			if (msg.msg[0] == FRMessageCodes.ARENA_SELECTED) {
				gameRef.setUpArena((int) msg.msg[1]);
				gameRef.moveToScreen(GameState.SELECT_CAR);
			}
		}
	}

	private void renderSelect(float delta) {
		tweenMgr.update(delta);
		batch.begin();
		endHint = FRAssets.arenaScreenAtlas.createSprite("endHint");
		renderTile(currentPosition);
		renderTile(currentPosition - 1);
		renderTile(currentPosition + 1);
		endHint.setSize(camHalfWidth * 2, camHalfHeight * 2);
		endHint.setPosition((currentPosition - 1) * camHalfWidth * 2, 0);
		if (showLeft) {
			endHint.draw(batch);
		}
		if (showRight) {
			endHint.flip(true, false);
			endHint.draw(batch);
		}
		batch.end();
	}

	private void updateSelect(float delta) {
		Message msg;
		while ((msg = gameRef.network.readFromClientQueue()) != null) {
			if (msg.msg[0] == FRMessageCodes.ARENA_SELECTED) {
				gameRef.setUpArena((int) msg.msg[1]);
				gameRef.moveToScreen(GameState.SELECT_CAR);
			}
		}
	}

	@Override
	public boolean touchDown(float x, float y, int pointer, int button) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean tap(float x, float y, int count, int button) {
		byte message[] = new byte[2];
		message[0] = FRMessageCodes.ARENA_SELECTED;
		message[1] = (byte) currentPosition;		
		gameRef.network.sendToClient(new Message(message, ""));
		return true;
	}

	@Override
	public boolean longPress(float x, float y) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean fling(float velocityX, float velocityY, int button) {
		if (velocityX > 5f) {
			moveLeft();
		} else if (velocityX < -5f) {
			moveRight();
		}
		return false;
	}

	private void moveLeft() {
		Gdx.app.log(FRConstants.TAG, "Move Left !");
		if (currentPosition > 1) {
			currentPosition = currentPosition - 1;
			Tween.to(camera, CameraTweenAccessor.POSITION, 1.0f)
					.target((currentPosition - 1) * camHalfWidth * 2
							+ camHalfWidth, camHalfHeight)
					.setCallback(myCallBack)
					.setCallbackTriggers(TweenCallback.END).start(tweenMgr);
			isAnimating = true;
		} else {
			showLeft = true;
			Timer.schedule(new Task() {
				public void run() {
					showLeft = false;
				}
			}, 0.3f);
		}
	}

	private void moveRight() {
		Gdx.app.log(FRConstants.TAG, "Move Right !");
		if (currentPosition < arenaNames.size()) {
			currentPosition = currentPosition + 1;
			Tween.to(camera, CameraTweenAccessor.POSITION, 1.0f)
					.target((currentPosition - 1) * camHalfWidth * 2
							+ camHalfWidth, camHalfHeight)
					.setCallback(myCallBack)
					.setCallbackTriggers(TweenCallback.END).start(tweenMgr);
			isAnimating = true;
		} else {
			showRight = true;
			Timer.schedule(new Task() {
				public void run() {
					showRight = false;
				}
			}, 0.3f);
		}
	}

	@Override
	public boolean pan(float x, float y, float deltaX, float deltaY) {
		return false;
	}

	@Override
	public boolean panStop(float x, float y, int pointer, int button) {
		return false;
	}

	@Override
	public boolean zoom(float initialDistance, float distance) {
		return false;
	}

	@Override
	public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2,
			Vector2 pointer1, Vector2 pointer2) {
		return false;
	}

	private void renderTile(int pos) {
		if (pos < 1 || pos > arenaNames.size()) {
			return;
		} else {
			batch.setProjectionMatrix(camera.combined);
			String name = arenaNames.get(pos - 1);
			logo = FRAssets.arenaScreenAtlas.createSprite(name + "Logo");
			heading = FRAssets.arenaScreenAtlas.createSprite(name + "Heading");
			trackOverview = FRAssets.arenaScreenAtlas.createSprite(name
					+ "Track");

			float xOffset = (pos - 1) * camHalfWidth * 2;

			background = FRAssets.background;
			background.setSize(camHalfWidth * 2, camHalfHeight * 2);
			background.setPosition(xOffset, 0);

			arrow = FRAssets.arenaScreenAtlas.createSprite("nextArrow");

			heading.setSize(5, 1);
			heading.setPosition(0.5f + xOffset, 8);

			logo.setSize(5, 4);
			logo.setPosition(1 + xOffset, 3.5f);

			trackOverview.setSize(3.5f, 2.0f);
			trackOverview.setPosition(xOffset + 1.25f, 1.0f);

			background.draw(batch);
			heading.draw(batch);
			logo.draw(batch);
			trackOverview.draw(batch);

			if (!isAnimating) {
				arrow.setSize(0.5f, 1.2f);
				if (currentPosition > 1) {
					arrow.setPosition(xOffset + 0.2f, camHalfHeight);
					arrow.draw(batch);
				}
				if (currentPosition < arenaNames.size()) {
					arrow.flip(true, false);
					arrow.setPosition(xOffset + 0.2f + 5f, camHalfHeight);
					arrow.draw(batch);
				}
			}

		}
	}

	TweenCallback myCallBack = new TweenCallback() {
		public void onEvent(int type, BaseTween<?> source) {
			isAnimating = false;
		}
	};
}<file_sep>package com.letsrace.game;

import com.badlogic.gdx.Gdx;

public class FRConstants {
	public final static int PIXELS_PER_UNIT = 16;
	public enum GameState {
		SPLASH,SPLASH_SIGN_IN, MENU, WAIT, SELECT_CAR, GAME_SCREEN, CAR_SELECT,MULTIPLAYER_MENU,ARENA_SELECT,PLAYER_WAITING};
	public final static int GUI_WIDTH = 480;
	public final static int GUI_HIEGHT = 800;
	public static float GUI_SCALE_WIDTH;
	public static final String TAG = "Lets-Race!";
	public static void initializeDynamicConstants(){
		GUI_SCALE_WIDTH = ((float)Gdx.graphics.getWidth()/GUI_WIDTH);
	}
}
<file_sep>package com.letsrace.game.network;

public interface FRMessageListener {
	public void onMessageRecieved(byte[] buffer, String participantId);
}
<file_sep>package com.letsrace.game.network;

import com.letsrace.game.GameWorld;
import com.letsrace.game.LetsRace;

public class Server {
	GameWorld world;
	LetsRace gameRef;
	enum ServerState {
		WAITING_FOR_ARENA_SELECT,
		CAR_SELECT,		
	}
}
<file_sep>package com.letsrace.game;

import java.util.HashMap;
import java.util.Set;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.World;
import com.letsrace.game.car.Car;
import com.letsrace.game.map.FRMapHandler;

public class GameWorld {
	public World physicalWorld;
	public HashMap<String, Player> players;
	public String arenaName;
	public FRMapHandler mapHandler;
	public int numberOfPlayers;
	int arenaNumber;

	public GameWorld() {
		this.physicalWorld = new World(new Vector2(0.0f, 0.0f), true);
		this.players = new HashMap<String, Player>();
		this.numberOfPlayers = 0;
	}

	public void setUpArena(String arenaName) {
		mapHandler = new FRMapHandler(physicalWorld,
				"beach_track_draft_two.tmx");
	}

	public void addPlayer(String id, String name, int playerType) {
		if (!players.containsKey(id)) {
			Player newPlayer = new Player();
			newPlayer.name = name;
			newPlayer.playerType = playerType;
			players.put(id, new Player());
			numberOfPlayers += 1;
		}
	}

	public void setUpCars() {
		Set keySet = players.keySet();
		int index = 0;
		for (Object key : keySet) {
			Player player = players.get(key);
			Vector2 initialPosition = mapHandler
					.getInitialPositionForNumber(index);
			player.car = new Car(physicalWorld, 3, 4, initialPosition, 0.0f,
					40, 20, 60, "hummer");
			index += 1;
		}
	}

}
<file_sep>package com.letsrace.game.network;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Set;

import com.letsrace.game.GameWorld;
import com.letsrace.game.Player;
import com.letsrace.game.car.Car;

public class ServerUtils {
	public static boolean isCarSelected(GameWorld world, int number) {
		Set keySet = world.players.keySet();
		for (Object key : keySet) {
			Player player = world.players.get(key);
			if (player.playerType == number) {
				return true;
			}
		}
		return false;
	}
	
	public static void updateCars(GameWorld world, float delta){
		Set keySet = world.players.keySet();
		for (Object key : keySet) {
			Car car = world.players.get(key).car;
			car.update(delta);
		}
	}

	public static byte[] generateCarSyncMessage(GameWorld gameWorld,
			ArrayList<String> participantIds) {
		int ctr = 0;
		byte playerNumber = 0;
		byte[] message = new byte[1 + participantIds.size() * 21];
		message[ctr++] = FRMessageCodes.SYNC_CARS;

		for (String participantId : participantIds) {
			Car c = gameWorld.players.get(participantId).car;
			message[ctr++] = playerNumber++;
			byte[] posX = ByteBuffer.allocate(4)
					.putFloat(c.getWorldPosition().x).array();
			for (int i = 0; i < 4; i++)
				message[ctr++] = posX[i];
			byte[] posY = ByteBuffer.allocate(4)
					.putFloat(c.getWorldPosition().y).array();
			for (int i = 0; i < 4; i++)
				message[ctr++] = posY[i];
			byte[] wAngle = ByteBuffer.allocate(4).putFloat(c.wheelAngle)
					.array();
			for (int i = 0; i < 4; i++)
				message[ctr++] = wAngle[i];
			byte[] cBodyAngle = ByteBuffer.allocate(4)
					.putFloat(c.getBodyAngle()).array();
			for (int i = 0; i < 4; i++)
				message[ctr++] = cBodyAngle[i];
			byte[] cSpeed = ByteBuffer.allocate(4).putFloat(c.getSpeedKMH())
					.array();
			for (int i = 0; i < 4; i++)
				message[ctr++] = cSpeed[i];
		}
		return message;

	}
}
<file_sep>Lets-Race
=========

New repo for versus-racer - multiplayer racing game based on libgdx
<file_sep>package com.letsrace.game.map;

import static com.letsrace.game.FRConstants.PIXELS_PER_UNIT;

import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.badlogic.gdx.physics.box2d.World;

public class FRMapHandler{
	public MapBodyManager manager;
	public TiledMap tiledMap;
	public World physicalWorldRef;
	public Vector2[] initialPositionMarkers;
	public FRAngleMonitor angleMon=new FRAngleMonitor(0.5f);
	public Body mainCarBody;
	
	public FRMapHandler(World physicalWorld,String arenaName){
		this.physicalWorldRef = physicalWorld;
		tiledMap = new TmxMapLoader().load(arenaName);
		manager = new MapBodyManager(physicalWorld, PIXELS_PER_UNIT, null, 0);
		manager.createPhysics(tiledMap);
		initialPositionMarkers = manager.getInitialPosition(tiledMap);
	}
	
	public void setupContactListener(Body mainCarBody){
		this.mainCarBody = mainCarBody;
		physicalWorldRef.setContactListener(new ContactListener() {
			public Vector2 lastBodyVector= new Vector2(0,0);
			public void preSolve(Contact contact, Manifold oldManifold) {}
			public void postSolve(Contact contact, ContactImpulse impulse) {}
			public void endContact(Contact contact) {}
			public void beginContact(Contact contact) {
				if(contact.getFixtureA().getBody() == FRMapHandler.this.mainCarBody||contact.getFixtureB().getBody() == FRMapHandler.this.mainCarBody){
					MarkerBody mb = manager.fetchMarkerBody(contact.getFixtureB().getBody());
					if(mb != null || (mb=manager.fetchMarkerBody(contact.getFixtureA().getBody()))!=null){
						angleMon.setTurnAngle(mb.direction.angle(lastBodyVector));
						lastBodyVector = mb.direction;
					}
				}
			
			}
			
		});
	}
	
	public Vector2 getInitialPositionForNumber(int index){
		return initialPositionMarkers[index];
	}
}
<file_sep>package com.letsrace.game.network;

import java.util.ArrayList;

public interface FRGoogleServices {
	void initiateSignIn();
	void startQuickGame();
	public ArrayList<String> getParticipantIds();
	public String getMyId();
	public void setServerListener(FRMessageListener listener);
	public void setClientListener(FRMessageListener listener);
	public void sendReliableMessage(byte[] message, String participantID);
	public void broadcastMessage(byte[] message);
	public void broadcastReliableMessage(byte[] message);
	public boolean isSignedIn();
	public String getSinglePlayerIds();
	
}	
<file_sep>package com.letsrace.game;

import static com.letsrace.game.FRConstants.PIXELS_PER_UNIT;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.maps.tiled.TiledMapRenderer;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.letsrace.game.car.Car;
import com.letsrace.game.map.OrthogonalTiledMapRenderer;

public class GameRenderer {
	GameWorld worldRef;
	Box2DDebugRenderer debugRenderer;
	OrthographicCamera cam;
	SpriteBatch batch;
	TiledMapRenderer tiledMapRenderer;
	Car myCar;

	public GameRenderer(GameWorld world, Batch batch, Car myCar) {
		worldRef = world;
		float w = Gdx.graphics.getWidth();
		float h = Gdx.graphics.getHeight();
		this.cam = new OrthographicCamera(PIXELS_PER_UNIT * 20, PIXELS_PER_UNIT
				* 20 * h / w);
		cam.position.set(cam.viewportWidth / 2f, cam.viewportHeight / 2f, 0);
		debugRenderer = new Box2DDebugRenderer();
		tiledMapRenderer = new OrthogonalTiledMapRenderer(world.mapHandler.tiledMap, batch);
		this.myCar = myCar;
	}

	public void render() {
		cam.update();
		cam.rotate(worldRef.mapHandler.angleMon.getTurnAngle());
		cam.position.set(myCar.getWorldPosition().x * PIXELS_PER_UNIT,myCar.getWorldPosition().y * PIXELS_PER_UNIT, 0);
		tiledMapRenderer.setView(cam);
		tiledMapRenderer.render();
		Matrix4 debugMat = new Matrix4(cam.combined);
		debugMat.scale(PIXELS_PER_UNIT, PIXELS_PER_UNIT, 1f);
		debugRenderer.render(worldRef.physicalWorld, debugMat);
	}
}
<file_sep>package com.letsrace.game.screen;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.utils.Timer;
import com.badlogic.gdx.utils.Timer.Task;
import com.letsrace.game.FRConstants;
import com.letsrace.game.FRConstants.GameState;
import com.letsrace.game.LetsRace;

public class FRSplashScreen extends ScreenAdapter {
	private LetsRace gameRef;

	public FRSplashScreen(LetsRace game) {
		Gdx.app.log(FRConstants.TAG, "FRSplash: Constructor");
		gameRef = game;
		Gdx.input.setInputProcessor(gameRef.stage);
		Image image = new Image(gameRef.skin.getDrawable("fr."));
		image.setSize(image.getWidth()*FRConstants.GUI_SCALE_WIDTH,image.getHeight()*FRConstants.GUI_SCALE_WIDTH);
		image.setPosition((Gdx.graphics.getWidth()-image.getWidth())/2, (Gdx.graphics.getHeight()-image.getHeight())/2);
		gameRef.stage.addActor(image);
	}

	public void show() {
		Gdx.app.log(FRConstants.TAG, "FRSplash: Show()");
		Timer.schedule(new Task() {
			@Override
			public void run() {
				gameRef.moveToScreen(GameState.MENU);
			}
		}, 3);
	}

	@Override
	public void render(float delta) {
		GL20 gl = Gdx.gl;
		gl.glClearColor(0, 0, 0, 1);
		gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		gameRef.stage.draw();
	}

	public void dispose() {
		Gdx.app.log(FRConstants.TAG, "Splash: Dispose()");
	}

}
<file_sep>package com.letsrace.game.unused;

import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.letsrace.game.Animation;

public class FRAssets {
	public static TextureAtlas uiButtonsAtlas;
	public static Sprite singleplayerButton;
	public static Sprite multiplayerButton;
	public static Sprite quickraceButton;
	public static Sprite invitefriendsButton;
	public static Sprite checkInvitesButton;
	public static Sprite settingsIcon;
	public static Sprite frIcon;
	public static Sprite background;
	public static Sprite signIn;
	public static Sprite signInPressed;

	public static TextureAtlas arenaScreenAtlas;
	public static TextureAtlas playerAsset;
	public static TextureAtlas carScreenAtlas;
	public static Animation explosion;

	public static Sprite cannister;
	public static Sprite vaderFullCar;
	public static Sprite firedMissile;
	public static Sprite vaderCarBody;
	public static Animation wheel;

	public static Sprite powerBar;
	public static Sprite accnBar;
	public static Sprite steeringBar;
	
	public static void load() {
		uiButtonsAtlas = new TextureAtlas("ui/ui_icons.pack");
		playerAsset = new TextureAtlas("ui/playerAssetsAtlas.txt");
		carScreenAtlas = new TextureAtlas("ui/carScreenAtlas.txt");
		powerBar = carScreenAtlas.createSprite("powerBar");
		steeringBar=carScreenAtlas.createSprite("steeringBar");
		accnBar=carScreenAtlas.createSprite("accelerationBar");
		explosion = new Animation(false, 1 / 60f, playerAsset, "explosion", 41);
		cannister = playerAsset.createSprite("cannister", 1);
		vaderFullCar = playerAsset.createSprite("vaderCar_NoWeapon");
		firedMissile = playerAsset.createSprite("missileFired");
		vaderCarBody = playerAsset.createSprite("vaderCarBody");
		wheel = new Animation(true, 1 / 7f, playerAsset, "wheel", 2);
		/*
		 * singleplayerButton =
		 * uiButtonsAtlas.createSprite("singlePlayerButton"); multiplayerButton
		 * = uiButtonsAtlas.createSprite("multiplayerButton"); quickraceButton =
		 * uiButtonsAtlas.createSprite("quickRaceButton"); invitefriendsButton =
		 * uiButtonsAtlas .createSprite("inviteFriendsButton");
		 * checkInvitesButton =
		 * uiButtonsAtlas.createSprite("checkInvitesButton"); settingsIcon =
		 * uiButtonsAtlas.createSprite("settingsIcon"); frIcon =
		 * uiButtonsAtlas.createSprite("fr."); background =
		 * uiButtonsAtlas.createSprite("background"); signIn
		 * =uiButtonsAtlas.createSprite("signIn"); signInPressed =
		 * uiButtonsAtlas.createSprite("signInPressed");
		 */
		background = uiButtonsAtlas.createSprite("background");
		arenaScreenAtlas = new TextureAtlas("ui/arenaScreenUI.txt");
	}

	public static void dispose() {
		if (uiButtonsAtlas != null)
			uiButtonsAtlas.dispose();
	}
}
<file_sep>package com.letsrace.game.car;

import java.util.ArrayList;
import java.util.List;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.World;
import com.letsrace.game.car.Wheel.WheelType;

public class Car {
	public Body body;
	public float width, length, angle, maxSteerAngle, maxSpeed, maxRevSpeed, power;
	public float wheelAngle;
	public Steer steer;
	public Accel accelerate;
	public List<Wheel> wheels;
	Vector2 origin;

	public enum Steer {
		NONE, LEFT, RIGHT
	};

	public enum Accel {
		NONE, ACCELERATE, BRAKE
	};

	public Car(World world, float width, float length, Vector2 position,
			float angle, float power, float maxSteerAngle, float maxSpeed,
			String jsonFilePrefix) {
		super();
		this.steer = Steer.NONE;
		this.accelerate = Accel.NONE;
		this.width = width;
		this.length = length;
		this.angle = angle;
		this.maxSteerAngle = maxSteerAngle;
		this.maxSpeed = maxSpeed;
		this.maxRevSpeed = maxSpeed / 2;
		this.power = power;
		this.wheelAngle = 0;
		BodyEditorLoader loader = new BodyEditorLoader(
				Gdx.files.internal(jsonFilePrefix + "-normal.json"));
		// init body
		BodyDef bodyDef = new BodyDef();
		bodyDef.type = BodyDef.BodyType.DynamicBody;
		bodyDef.position.set(position);
		bodyDef.angle = angle;
		this.body = world.createBody(bodyDef);
		origin = loader.getOrigin("Name", width);
		// init shape
		FixtureDef fixtureDef = new FixtureDef();
		fixtureDef.density = 1.0f;
		fixtureDef.friction = 0.6f; // friction when rubbing against other
									// shapes
		fixtureDef.restitution = 0.4f; // amount of force feedback when hitting
										// something. >0 makes the car bounce
										// off, it's fun!
		loader.attachFixture(this.body, "Name", fixtureDef, this.width);
		// initialize wheels
		this.wheels = new ArrayList<Wheel>();
		this.wheels.add(new Wheel(world, this, this.width, false, false,
				jsonFilePrefix, WheelType.BOTTOM_LEFT)); // top left

		this.wheels.add(new Wheel(world, this, this.width, false, false,
				jsonFilePrefix, WheelType.BOTTOM_RIGHT)); // top right

		this.wheels.add(new Wheel(world, this, this.width, true, true,
				jsonFilePrefix, WheelType.TOP_LEFT)); // back left
		this.wheels.add(new Wheel(world, this, this.width, true, true,
				jsonFilePrefix, WheelType.TOP_RIGHT)); // back right
	}

	public List<Wheel> getPoweredWheels() {
		List<Wheel> poweredWheels = new ArrayList<Wheel>();
		for (Wheel wheel : this.wheels) {
			if (wheel.powered)
				poweredWheels.add(wheel);
		}
		return poweredWheels;
	}

	public Vector2 getLocalVelocity() {
		/*
		 * returns car's velocity vector relative to the car
		 */
		return this.body.getLocalVector(this.body
				.getLinearVelocityFromLocalPoint(new Vector2(0, 0)));
	}

	public Vector2 getWorldPosition() {
		return this.body.getPosition();
	}

	public float getRotation() {
		return this.body.getAngle();
	}

	public List<Wheel> getRevolvingWheels() {
		List<Wheel> revolvingWheels = new ArrayList<Wheel>();
		for (Wheel wheel : this.wheels) {
			if (wheel.revolving)
				revolvingWheels.add(wheel);
		}
		return revolvingWheels;
	}

	public float getSpeedKMH() {
		Vector2 velocity = this.body.getLinearVelocity();
		float len = velocity.len();
		return (len / 1000) * 3600;
	}
	
	public float getBodyAngle(){
		return this.body.getAngle();
	}

	public void setSpeed(float speed) {
		/*
		 * speed - speed in kilometers per hour
		 */
		Vector2 velocity = this.body.getLinearVelocity();
		velocity = velocity.nor();
		velocity = new Vector2(velocity.x * ((speed * 1000.0f) / 3600.0f),
				velocity.y * ((speed * 1000.0f) / 3600.0f));
		this.body.setLinearVelocity(velocity);
	}

	public void update(float deltaTime) {
		// 1. KILL SIDEWAYS VELOCITY

		for (Wheel wheel : wheels) {
			wheel.killSidewaysVelocity();
		}

		// 2. SET WHEEL ANGLE

		// calculate the change in wheel's angle for this update
		float incr = (this.maxSteerAngle) * deltaTime * 5;

		if (this.steer == Steer.LEFT) {
			this.wheelAngle = Math.min(Math.max(this.wheelAngle, 0) + incr,
					this.maxSteerAngle); // increment angle without going over
											// max steer
		} else if (this.steer == Steer.RIGHT) {
			this.wheelAngle = Math.max(Math.min(this.wheelAngle, 0) - incr,
					-this.maxSteerAngle); // decrement angle without going over
											// max steer
		} else {
			this.wheelAngle = 0;
		}

		// update revolving wheels
		for (Wheel wheel : this.getRevolvingWheels()) {
			wheel.setAngle(this.wheelAngle);
		}

		// 3. APPLY FORCE TO WHEELS
		Vector2 baseVector = new Vector2(0, 0); // vector pointing in the
												// direction force will be
		// applied to a wheel ; relative to the wheel.

		// if accelerator is pressed down and speed limit has not been reached,
		// go forwards
		if ((this.accelerate == Accel.ACCELERATE)
				&& (this.getSpeedKMH() < this.maxSpeed)) {
			baseVector = new Vector2(0, 1);
		} else if (this.accelerate == Accel.BRAKE) {
			// braking, but still moving forwards - increased force
			if (this.getLocalVelocity().y > 0) {
				baseVector = new Vector2(0f, -1.3f);
			}
			// going in reverse - less force
			else if ((this.getSpeedKMH() < this.maxRevSpeed))
				baseVector = new Vector2(0f, -0.7f);
		} else if (this.accelerate == Accel.NONE) {
			// slow down if not accelerating
			baseVector = new Vector2(0, 0);
			if (this.getSpeedKMH() < 7)
				this.setSpeed(0);
			else if (this.getLocalVelocity().y > 0)
				baseVector = new Vector2(0, -0.7f);
			else if (this.getLocalVelocity().y < 0)
				baseVector = new Vector2(0, +0.7f);
		} else
			baseVector = new Vector2(0, 0);
		// multiply by engine power, which gives us a force vector relative to
		// the wheel
		Vector2 forceVector = new Vector2(this.power * baseVector.x, this.power
				* baseVector.y);

		// apply force to each wheel
		for (Wheel wheel : this.getPoweredWheels()) {
			Vector2 position = wheel.body.getWorldCenter();
			wheel.body.applyForce(wheel.body.getWorldVector(new Vector2(
					forceVector.x, forceVector.y)), position, true);
		}

	}

	public void setTransform(float posX, float posY, float cBodyAngle) {
		this.body.setTransform(posX, posY, cBodyAngle);
	}
	
}
<file_sep>package com.letsrace.game;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.letsrace.game.FRConstants.GameState;
import com.letsrace.game.network.FRGameClient;
import com.letsrace.game.network.FRGameServer;
import com.letsrace.game.network.FRGoogleServices;
import com.letsrace.game.network.FRNetwork;
import com.letsrace.game.screen.CarSelectScreen;
import com.letsrace.game.screen.FRArenaSelectScreen;
import com.letsrace.game.screen.FRGameScreen;
import com.letsrace.game.screen.FRMenuScreen;
import com.letsrace.game.screen.FRMultiplayerMenuScreen;
import com.letsrace.game.screen.FRSplashScreen;
import com.letsrace.game.screen.FRWaitScreen;
import com.letsrace.game.screen.FRWaitingForPlayerScreen;
import com.letsrace.game.screen.GameScreen;
import com.letsrace.game.unused.FRAssets;

public class LetsRace extends Game {
	public GameState gameState;
	public BitmapFont font;
	public FRGoogleServices googleServices;
	public Stage stage;
	public Camera guicam;
	public SpriteBatch batch;
	public Skin skin;
	public HashMap<String, Integer> playerNumber;
	public int myPlayerNo;
	public FRGameClient client;
	public boolean multiplayer;
	public FRNetwork network;
	public String serverId;
	public String myId;
	public String arenaName;

	public GameWorld serverWorld;
	public GameWorld clientWorld;

	public void setServerId(String serverId) {
		this.serverId = serverId;
	}

	public void setMyId(String myId) {
		this.myId = myId;
	}

	public void setArenaName(String arenaName) {
		this.arenaName = arenaName;
	}

	public boolean isServer() {
		if (serverId == null || myId == null) {
			return false;
		}
		return myId.equals(serverId);
	}

	public LetsRace(FRGoogleServices services) {
		googleServices = services;
		playerNumber = new HashMap<String, Integer>();
		network = new FRNetwork(this);
	}

	public void setUpWorlds() {
		if (isServer()) {
			serverWorld = new GameWorld();
			clientWorld = new GameWorld();
			for (String id : this.googleServices.getParticipantIds()) {
				serverWorld.addPlayer(id, id, -1);
				clientWorld.addPlayer(id, id, -1);
			}
		} else {
			clientWorld = new GameWorld();
			for (String id : googleServices.getParticipantIds()) {
				clientWorld.addPlayer(id, id, -1);
			}
		}
	}

	public void setUpArena(int arenaNumber) {
		if (isServer()) {
			this.serverWorld.arenaNumber = arenaNumber;
			this.serverWorld.setUpArena(arenaName);
			this.clientWorld.arenaNumber = arenaNumber;
			this.clientWorld.setUpArena(arenaName);
		} else {
			this.clientWorld.arenaNumber = arenaNumber;
			this.clientWorld.setUpArena(arenaName);
		}
	}

	public void setUpCars() {
		if (isServer()) {
			this.serverWorld.setUpCars();
			this.clientWorld.setUpCars();
		} else {
			this.clientWorld.setUpCars();
		}
	}

	public void decideOnServerAndStart() {
		ArrayList<String> list = googleServices.getParticipantIds();
		String[] participants = list.toArray(new String[list.size()]);
		Arrays.sort(participants);
		if (participants[0] == googleServices.getMyId()) {
			Gdx.app.log(FRConstants.TAG, "I am the server");
			FRGameServer server = new FRGameServer(this, participants);
			googleServices.setServerListener(server);
		}
		int ctr = 0;
		for (String s : participants) {
			playerNumber.put(s, ctr++);
		}
		myPlayerNo = playerNumber.get(googleServices.getMyId());
		Gdx.app.log(FRConstants.TAG, "I am the client - P" + myPlayerNo);
		client = new FRGameClient(this, participants[0]);
		Gdx.app.log(FRConstants.TAG, "Setting Client Listener");
		googleServices.setClientListener(client);
		Gdx.app.log(FRConstants.TAG, "Moving to car-select screen");
		moveToScreen(GameState.SELECT_CAR);
	}

	@Override
	public void create() {
		font = new BitmapFont();
		skin = new Skin(new TextureAtlas("ui/ui_icons.pack"));
		stage = new Stage();
		guicam = new OrthographicCamera();
		batch = new SpriteBatch();
		FRConstants.initializeDynamicConstants();
		FRAssets.load();
		moveToScreen(GameState.SPLASH);
	}

	@Override
	public void render() {
		super.render();
	}

	public void dispose() {
		stage.dispose();
		font.dispose();
	}

	public void moveToScreen(GameState screen) {
		switch (screen) {
		case GAME_SCREEN:
			gameState = GameState.GAME_SCREEN;
			setScreen(new GameScreen(this));
			break;
		case MENU:
			gameState = GameState.MENU;
			setScreen(new FRMenuScreen(this));
			break;
		case WAIT:
			gameState = GameState.WAIT;
			setScreen(new FRWaitScreen(this));
			break;
		case SELECT_CAR:
			gameState = GameState.SELECT_CAR;
			setScreen(new CarSelectScreen(this));
			break;
		case SPLASH:
			gameState = GameState.SPLASH;
			setScreen(new FRSplashScreen(this));
			break;
		case MULTIPLAYER_MENU:
			gameState = GameState.MULTIPLAYER_MENU;
			setScreen(new FRMultiplayerMenuScreen(this));
			break;
		case ARENA_SELECT:
			gameState = GameState.ARENA_SELECT;
			setScreen(new FRArenaSelectScreen(this));
			break;
		case PLAYER_WAITING:
			gameState = GameState.PLAYER_WAITING;
			setScreen(new FRWaitingForPlayerScreen(this));
			break;
		default:
			break;
		}
	}
}
<file_sep>package com.letsrace.game.unused;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle;
import com.letsrace.game.FRConstants;
import com.letsrace.game.LetsRace;

public class FROldSplashScreen extends ScreenAdapter{
	private LetsRace gameRef;
	
	public FROldSplashScreen(LetsRace game){
		Gdx.app.log(FRConstants.TAG, "Splash: Constructor");
		gameRef = game;
		gameRef.stage.clear();
		Gdx.input.setInputProcessor(gameRef.stage);
		Image image = new Image(gameRef.skin.getDrawable("wait-screen-back"));
		image.setWidth(Gdx.graphics.getWidth());
		image.setHeight(Gdx.graphics.getHeight());
		LabelStyle style = new LabelStyle(gameRef.font, Color.WHITE);
		Label label = new Label("Signing in ...", style);
		label.setPosition((Gdx.graphics.getWidth()-label.getWidth())/2,(Gdx.graphics.getHeight())/2+label.getHeight());
		gameRef.stage.addActor(image);
		gameRef.stage.addActor(label);
	}
	
	public void show(){
		Gdx.app.log(FRConstants.TAG, "Splash: Show()");
		// Throwing stuff in constructor as loading stuff in show() causes black screen
		gameRef.googleServices.initiateSignIn();
	}
	
	@Override
	public void render(float delta) {
		GL20 gl = Gdx.gl;
		gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		gameRef.stage.draw();
	}
	
	public void dispose() {
		Gdx.app.log(FRConstants.TAG, "Splash: Dispose()");
	}
	
}
<file_sep>package com.letsrace.game.android;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import android.app.Activity;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.WindowManager;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
import com.badlogic.gdx.utils.Timer;
import com.badlogic.gdx.utils.Timer.Task;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.games.Games;
import com.google.android.gms.games.GamesActivityResultCodes;
import com.google.android.gms.games.GamesStatusCodes;
import com.google.android.gms.games.multiplayer.Invitation;
import com.google.android.gms.games.multiplayer.Multiplayer;
import com.google.android.gms.games.multiplayer.OnInvitationReceivedListener;
import com.google.android.gms.games.multiplayer.Participant;
import com.google.android.gms.games.multiplayer.realtime.RealTimeMessage;
import com.google.android.gms.games.multiplayer.realtime.Room;
import com.google.android.gms.games.multiplayer.realtime.RoomConfig;
import com.google.android.gms.games.multiplayer.realtime.RoomStatusUpdateListener;
import com.google.android.gms.games.multiplayer.realtime.RoomUpdateListener;
import com.google.android.gms.plus.Plus;
import com.google.example.games.basegameutils.BaseGameUtils;
import com.letsrace.game.FRConstants.GameState;
import com.letsrace.game.LetsRace;
import com.letsrace.game.network.FRGoogleServices;
import com.letsrace.game.network.FRMessageListener;
import com.letsrace.game.screen.FRMultiplayerMenuScreen;
import com.letsrace.game.unused.FRAssets;

public class AndroidLauncher extends AndroidApplication implements
		FRGoogleServices, GoogleApiClient.ConnectionCallbacks,
		GoogleApiClient.OnConnectionFailedListener, RoomStatusUpdateListener,
		RoomUpdateListener, OnInvitationReceivedListener {
	/*
	 * API INTEGRATION SECTION. This section contains the code that integrates
	 * the game with the Google Play game services API.
	 */
	final static String TAG = "Lets-Race!";

	// Request codes for the UIs that we show with startActivityForResult:
	final static int RC_SELECT_PLAYERS = 10000;
	final static int RC_INVITATION_INBOX = 10001;
	final static int RC_WAITING_ROOM = 10002;

	// Request code used to invoke sign in user interactions.
	private static final int RC_SIGN_IN = 9001;

	private GoogleApiClient mGoogleApiClient;

	// Are we currently resolving a connection failure?
	private boolean mResolvingConnectionFailure = false;

	// Has the user clicked the sign-in button?
	private boolean mSignInClicked = false;

	// Set to true to automatically start the sign in flow when the Activity
	// starts.
	// Set to false to require the user to click the button in order to sign in.
	private boolean mAutoStartSignInFlow = true;

	// Room ID where the currently active game is taking place; null if we're
	// not playing.
	Room room;

	// Are we playing in multiplayer mode?
	boolean mMultiplayer = false;

	// The participants in the currently active game
	ArrayList<Participant> mParticipants = null;

	// My participant ID in the currently active game
	String mMyId = null;

	// If non-null, this is the id of the invitation we received via the
	// invitation listener
	String mIncomingInvitationId = null;

	// Message handler
	FRMessageHandler messageHandler;

	LetsRace game;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		// Create the Google Api Client with access to Plus and Games
		mGoogleApiClient = new GoogleApiClient.Builder(this)
				.addConnectionCallbacks(this)
				.addOnConnectionFailedListener(this).addApi(Plus.API)
				.addScope(Plus.SCOPE_PLUS_LOGIN).addApi(Games.API)
				.addScope(Games.SCOPE_GAMES).build();
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
		AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
		config.useAccelerometer = true;
		config.useCompass = true;
		game = new LetsRace(this);
		messageHandler = new FRMessageHandler(game.network);
		initialize(game, config);
	}

	public ArrayList<Participant> getmParticipants() {
		return mParticipants;
	}

	@Override
	protected void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
	}

	public void initiateSignIn() {
		// start the sign-in flow
		Log.d(TAG, "Sign-in button clicked");
		mSignInClicked = true;
		mGoogleApiClient.connect();
	}

	public void invitePlayers() {
		// show list of invitable players
		Intent intent = Games.RealTimeMultiplayer.getSelectOpponentsIntent(
				mGoogleApiClient, 1, 3);
		game.moveToScreen(GameState.WAIT);
		startActivityForResult(intent, RC_SELECT_PLAYERS);
	}

	public void showInvites() {
		Intent intent = Games.Invitations
				.getInvitationInboxIntent(mGoogleApiClient);
		game.moveToScreen(GameState.WAIT);
		startActivityForResult(intent, RC_INVITATION_INBOX);
	}

	public void startQuickGame() {
		// quick-start a game with 1 randomly selected opponent
		final int MIN_OPPONENTS = 1, MAX_OPPONENTS = 1;
		Bundle autoMatchCriteria = RoomConfig.createAutoMatchCriteria(
				MIN_OPPONENTS, MAX_OPPONENTS, 0);
		RoomConfig.Builder rtmConfigBuilder = RoomConfig.builder(this);
		rtmConfigBuilder.setMessageReceivedListener(messageHandler);
		rtmConfigBuilder.setRoomStatusUpdateListener(this);
		rtmConfigBuilder.setAutoMatchCriteria(autoMatchCriteria);
		game.moveToScreen(GameState.WAIT);
		Games.RealTimeMultiplayer.create(mGoogleApiClient,
				rtmConfigBuilder.build());
	}

	@Override
	public void onActivityResult(int requestCode, int responseCode,
			Intent intent) {
		super.onActivityResult(requestCode, responseCode, intent);
		Gdx.app.log(TAG, "On activity result: " + requestCode);
		switch (requestCode) {
		case RC_SELECT_PLAYERS:
			// we got the result from the "select players" UI -- ready to create
			// the room
			handleSelectPlayersResult(responseCode, intent);
			break;
		case RC_INVITATION_INBOX:
			// we got the result from the "select invitation" UI (invitation
			// inbox). We're
			// ready to accept the selected invitation:
			handleInvitationInboxResult(responseCode, intent);
			break;
		case RC_WAITING_ROOM:
			// we got the result from the "waiting room" UI.
			if (responseCode == Activity.RESULT_OK) {
				// ready to start playing
				Log.d(TAG, "Starting game (waiting room returned OK).");
				game.moveToScreen(GameState.GAME_SCREEN);
			} else if (responseCode == GamesActivityResultCodes.RESULT_LEFT_ROOM) {
				// player indicated that they want to leave the room
				leaveRoom();
				game.moveToScreen(GameState.MENU);
			} else if (responseCode == Activity.RESULT_CANCELED) {
				// Dialog was cancelled (user pressed back key, for instance).
				// In our game,
				// this means leaving the room too. In more elaborate games,
				// this could mean
				// something else (like minimizing the waiting room UI).
				leaveRoom();
				game.moveToScreen(GameState.MENU);
			}
			break;
		case RC_SIGN_IN:
			Log.d(TAG,
					"onActivityResult with requestCode == RC_SIGN_IN, responseCode="
							+ responseCode + ", intent=" + intent);
			mSignInClicked = false;
			mResolvingConnectionFailure = false;
			if (responseCode == RESULT_OK) {
				mGoogleApiClient.connect();
			} else {
				OnClickListener listener = new OnClickListener() {
					Activity parentActivity = AndroidLauncher.this;

					@Override
					public void onClick(DialogInterface dialog, int which) {
						parentActivity.finish();
					}
				};
				BaseGameUtils.showActivityResultError(this, listener,
						requestCode, responseCode, R.string.sign_in_error,
						responseCode);
			}
			break;
		}
		super.onActivityResult(requestCode, responseCode, intent);

	}

	// Handle the result of the "Select players UI" we launched when the user
	// clicked the
	// "Invite friends" button. We react by creating a room with those players.
	private void handleSelectPlayersResult(int response, Intent data) {
		if (response != Activity.RESULT_OK) {
			Log.w(TAG, "*** select players UI cancelled, " + response);
			game.moveToScreen(GameState.MENU);
			return;
		}

		Log.d(TAG, "Select players UI succeeded.");

		// get the invitee list
		final ArrayList<String> invitees = data
				.getStringArrayListExtra(Games.EXTRA_PLAYER_IDS);
		Log.d(TAG, "Invitee count: " + invitees.size());

		// get the automatch criteria
		Bundle autoMatchCriteria = null;
		int minAutoMatchPlayers = data.getIntExtra(
				Multiplayer.EXTRA_MIN_AUTOMATCH_PLAYERS, 0);
		int maxAutoMatchPlayers = data.getIntExtra(
				Multiplayer.EXTRA_MAX_AUTOMATCH_PLAYERS, 0);
		if (minAutoMatchPlayers > 0 || maxAutoMatchPlayers > 0) {
			autoMatchCriteria = RoomConfig.createAutoMatchCriteria(
					minAutoMatchPlayers, maxAutoMatchPlayers, 0);
			Log.d(TAG, "Automatch criteria: " + autoMatchCriteria);
		}

		// create the room
		Log.d(TAG, "Creating room...");
		RoomConfig.Builder rtmConfigBuilder = RoomConfig.builder(this);
		rtmConfigBuilder.addPlayersToInvite(invitees);
		rtmConfigBuilder.setMessageReceivedListener(messageHandler);
		rtmConfigBuilder.setRoomStatusUpdateListener(this);
		if (autoMatchCriteria != null) {
			rtmConfigBuilder.setAutoMatchCriteria(autoMatchCriteria);
		}
		game.moveToScreen(GameState.WAIT);
		Games.RealTimeMultiplayer.create(mGoogleApiClient,
				rtmConfigBuilder.build());
		Log.d(TAG, "Room created, waiting for it to be ready...");
	}

	// Handle the result of the invitation inbox UI, where the player can pick
	// an invitation
	// to accept. We react by accepting the selected invitation, if any.
	private void handleInvitationInboxResult(int response, Intent data) {
		if (response != Activity.RESULT_OK) {
			Log.w(TAG, "*** invitation inbox UI cancelled, " + response);
			game.moveToScreen(GameState.MENU);
			return;
		}

		Log.d(TAG, "Invitation inbox UI succeeded.");
		Invitation inv = data.getExtras().getParcelable(
				Multiplayer.EXTRA_INVITATION);

		// accept invitation
		acceptInviteToRoom(inv.getInvitationId());
	}

	// Accept the given invitation.
	void acceptInviteToRoom(String invId) {
		// accept the invitation
		Log.d(TAG, "Accepting invitation: " + invId);
		RoomConfig.Builder roomConfigBuilder = RoomConfig.builder(this);
		roomConfigBuilder.setInvitationIdToAccept(invId)
				.setMessageReceivedListener(messageHandler)
				.setRoomStatusUpdateListener(this);
		game.moveToScreen(GameState.WAIT);
		Games.RealTimeMultiplayer.join(mGoogleApiClient,
				roomConfigBuilder.build());
	}

	// Activity is going to the background. We have to leave the current room.
	@Override
	public void onStop() {
		Log.d(TAG, "**** got onStop");
		// if we're in a room, leave it.
		leaveRoom();
		super.onStop();
		FRAssets.dispose();
	}

	// Activity just got to the foreground. We switch to the wait screen because
	// we will now
	// go through the sign-in flow (remember that, yes, every time the Activity
	// comes back to the
	// foreground we go through the sign-in flow -- but if the user is already
	// authenticated,
	// this flow simply succeeds and is imperceptible).
	@Override
	public void onStart() {
		// TODO: Implement logic
		// game.goToWaitScreen();
		// if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
		// Log.w(TAG, "GameHelper: client was already connected on onStart()");
		// } else {
		// Log.d(TAG, "Connecting client.");
		// mGoogleApiClient.connect();
		// }
		super.onStart();
	}

	// Handle back key to make sure we cleanly leave a game if we are in the
	// middle of one
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent e) {
		if (keyCode == KeyEvent.KEYCODE_BACK
				&& game.gameState == GameState.GAME_SCREEN) {
			leaveRoom();
			return true;
		}
		return super.onKeyDown(keyCode, e);
	}

	// Leave the room.
	void leaveRoom() {
		Log.d(TAG, "Leaving room.");
		if (room != null) {
			Games.RealTimeMultiplayer.leave(mGoogleApiClient, this,
					room.getRoomId());
			room = null;
		}
	}

	// Show the waiting room UI to track the progress of other players as they
	// enter the
	// room and get connected.
	void showWaitingRoom(Room room) {
		// minimum number of players required for our game
		// For simplicity, we require everyone to join the game before we start
		// it
		// (this is signaled by Integer.MAX_VALUE).
		final int MIN_PLAYERS = Integer.MAX_VALUE;
		Intent i = Games.RealTimeMultiplayer.getWaitingRoomIntent(
				mGoogleApiClient, room, MIN_PLAYERS);

		// show waiting room UI
		startActivityForResult(i, RC_WAITING_ROOM);
	}

	// Called when we get an invitation to play a game. We react by showing that
	// to the user.
	@Override
	public void onInvitationReceived(Invitation invitation) {
		// We got an invitation to play a game! So, store it in
		// mIncomingInvitationId
		// and show the popup on the screen.
		// TODO: implement this
	}

	@Override
	public void onInvitationRemoved(String invitationId) {
		if (mIncomingInvitationId.equals(invitationId)) {
			mIncomingInvitationId = null;
		}
		// TODO: implement this
	}

	/*
	 * CALLBACKS SECTION. This section shows how we implement the several games
	 * API callbacks.
	 */

	@Override
	public void onConnected(Bundle connectionHint) {
		Log.d(TAG, "onConnected() called. Sign in successful!");
		Log.d(TAG, "Sign-in succeeded.");
		// register listener so we are notified if we receive an invitation to
		// play
		// while we are in the game
		Games.Invitations.registerInvitationListener(mGoogleApiClient, this);
		if (connectionHint != null) {
			Log.d(TAG,
					"onConnected: connection hint provided. Checking for invite.");
			Invitation inv = connectionHint
					.getParcelable(Multiplayer.EXTRA_INVITATION);
			if (inv != null && inv.getInvitationId() != null) {
				// retrieve and cache the invitation ID
				Log.d(TAG, "onConnected: connection hint has a room invite!");
				acceptInviteToRoom(inv.getInvitationId());
				return;
			}
		}
		// TODO: Pop up already signed in
		Gdx.app.postRunnable(new Runnable() {
			@Override
			public void run() {
				if (game.gameState == GameState.MULTIPLAYER_MENU) {
					((FRMultiplayerMenuScreen) game.getScreen())
							.enableSignedInButtons();
				}
			}
		});
	}

	@Override
	public void onConnectionSuspended(int i) {
		Log.d(TAG, "onConnectionSuspended() called. Trying to reconnect.");
		mGoogleApiClient.connect();
	}

	@Override
	public void onConnectionFailed(ConnectionResult connectionResult) {
		Log.d(TAG, "onConnectionFailed() called, result: " + connectionResult);

		if (mResolvingConnectionFailure) {
			Log.d(TAG,
					"onConnectionFailed() ignoring connection failure; already resolving.");
			return;
		}

		if (mSignInClicked || mAutoStartSignInFlow) {
			mAutoStartSignInFlow = false;
			mSignInClicked = false;
			mResolvingConnectionFailure = BaseGameUtils
					.resolveConnectionFailure(this, mGoogleApiClient,
							connectionResult, RC_SIGN_IN,
							getString(R.string.other_error));
		}

		// TODO: Quit game
	}

	// Called when we are connected to the room. We're not ready to play yet!
	// (maybe not everybody
	// is connected yet).
	@Override
	public void onConnectedToRoom(Room room) {
		Log.d(TAG, "onConnectedToRoom.");

		// get room ID, participants and my ID:
		this.room = room;
		mParticipants = room.getParticipants();
		mMyId = room.getParticipantId(Games.Players
				.getCurrentPlayerId(mGoogleApiClient));

		// print out the list of participants (for debug purposes)
		Log.d(TAG, "Room ID: " + room.getRoomId());
		Log.d(TAG, "My ID " + mMyId);
		Log.d(TAG, "<< CONNECTED TO ROOM >>");
	}

	// Called when we've successfully left the room (this happens a result of
	// voluntarily leaving
	// via a call to leaveRoom(). If we get disconnected, we get
	// onDisconnectedFromRoom()).
	@Override
	public void onLeftRoom(int statusCode, String roomId) {
		// we have left the room; return to main screen.
		Log.d(TAG, "onLeftRoom, code " + statusCode);
		game.moveToScreen(GameState.MENU);
	}

	// Called when we get disconnected from the room. We return to the main
	// screen.
	@Override
	public void onDisconnectedFromRoom(Room room) {
		room = null;
		showGameError();
	}

	// Show error message about game being cancelled and return to main screen.
	void showGameError() {
		OnClickListener listener = new OnClickListener() {
			@Override
			public void onClick(DialogInterface arg0, int arg1) {
			}
		};
		BaseGameUtils.makeSimpleDialog(this, getString(R.string.other_error),
				listener).show();
	}

	// Called when room has been created
	@Override
	public void onRoomCreated(int statusCode, Room room) {
		Log.d(TAG, "onRoomCreated(" + statusCode + ", " + room + ")");
		if (statusCode != GamesStatusCodes.STATUS_OK) {
			Log.e(TAG, "*** Error: onRoomCreated, status " + statusCode);
			showGameError();
			return;
		}

		// show the waiting room UI
		showWaitingRoom(room);
	}

	// Called when room is fully connected.
	@Override
	public void onRoomConnected(int statusCode, Room room) {
		Log.d(TAG, "onRoomConnected(" + statusCode + ", " + room + ")");
		if (statusCode != GamesStatusCodes.STATUS_OK) {
			Log.e(TAG, "*** Error: onRoomConnected, status " + statusCode);
			showGameError();
			return;
		}
		updateRoom(room);
	}

	@Override
	public void onJoinedRoom(int statusCode, Room room) {
		Log.d(TAG, "onJoinedRoom(" + statusCode + ", " + room + ")");
		if (statusCode != GamesStatusCodes.STATUS_OK) {
			Log.e(TAG, "*** Error: onRoomConnected, status " + statusCode);
			showGameError();
			return;
		}

		// show the waiting room UI
		showWaitingRoom(room);
	}

	// We treat most of the room update callbacks in the same way: we update our
	// list of
	// participants and update the display. In a real game we would also have to
	// check if that
	// change requires some action like removing the corresponding player avatar
	// from the screen,
	// etc.
	@Override
	public void onPeerDeclined(Room room, List<String> arg1) {
		updateRoom(room);
	}

	@Override
	public void onPeerInvitedToRoom(Room room, List<String> arg1) {
		updateRoom(room);
	}

	@Override
	public void onP2PDisconnected(String participant) {
	}

	@Override
	public void onP2PConnected(String participant) {
	}

	@Override
	public void onPeerJoined(Room room, List<String> arg1) {
		updateRoom(room);
	}

	@Override
	public void onPeerLeft(Room room, List<String> peersWhoLeft) {
		updateRoom(room);
	}

	@Override
	public void onRoomAutoMatching(Room room) {
		updateRoom(room);
	}

	@Override
	public void onRoomConnecting(Room room) {
		updateRoom(room);
	}

	@Override
	public void onPeersConnected(Room room, List<String> peers) {
		updateRoom(room);
	}

	@Override
	public void onPeersDisconnected(Room room, List<String> peers) {
		updateRoom(room);
	}

	void updateRoom(Room room) {
		if (room != null) {
			mParticipants = room.getParticipants();
		}
	}

	public ArrayList<String> getParticipantIds() {
		if (!game.multiplayer) {
			ArrayList<String> local = new ArrayList<String>();
			local.add("local");
			return local;
		} else {
			ArrayList<String> participants = room.getParticipantIds();
			participants.sort(new Comparator<String>() {
				public int compare(String o1, String o2) {
					return o1.compareTo(o2);
				}
			});
			return participants;
		}
	}

	public String getMyId() {
		return mMyId;
	}

	public void sendReliableMessage(byte[] message, String participantID) {
		if (participantID.equals(mMyId)) {
			Gdx.app.log(TAG, "Manual trigger onRealTimeMessageRecieved()");
			triggerMessageRecieveWithDelay(message);
		} else
			Games.RealTimeMultiplayer.sendReliableMessage(mGoogleApiClient,
					null, message, room.getRoomId(), participantID);
	}

	public void broadcastMessage(byte[] message) {
		for (Participant p : mParticipants) {
			if (p.getParticipantId().equals(mMyId)) {
				Gdx.app.log(TAG, "Manual trigger onRealTimeMessageRecieved()");
				triggerMessageRecieveWithDelay(message);
			} else
				Games.RealTimeMultiplayer.sendUnreliableMessage(
						mGoogleApiClient, message, room.getRoomId(),
						p.getParticipantId());
		}
	}

	@Override
	public void broadcastReliableMessage(byte[] message) {
		for (Participant p : mParticipants) {
			if (p.getParticipantId().equals(mMyId)) {
				Gdx.app.log(TAG, "Manual trigger onRealTimeMessageRecieved()");
				triggerMessageRecieveWithDelay(message);
			} else
				Games.RealTimeMultiplayer.sendReliableMessage(mGoogleApiClient,
						null, message, room.getRoomId(), p.getParticipantId());
		}
	}

	public void triggerMessageRecieveWithDelay(final byte[] message) {
		Timer.schedule(new Task() {
			@Override
			public void run() {
				messageHandler.onRealTimeMessageReceived(new RealTimeMessage(
						mMyId, message, RealTimeMessage.RELIABLE));
			}
		}, 0.1f);
	}

	@Override
	public void setServerListener(FRMessageListener listener) {
		messageHandler.setServerMessageListener(listener);
	}

	@Override
	public void setClientListener(FRMessageListener listener) {
		messageHandler.setClientMessageListener(listener);
	}

	@Override
	public boolean isSignedIn() {
		return mGoogleApiClient != null && mGoogleApiClient.isConnected();
	}

	@Override
	public String getSinglePlayerIds() {
		return "local";
	}
}
<file_sep>package com.letsrace.game.network;

public class FRPlayerData {
	int carCode;
	int ping;
	public FRPlayerData() {
		carCode = Integer.MIN_VALUE;
		ping = Integer.MIN_VALUE;
	}
}
<file_sep>package com.letsrace.game.screen;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.GL20;
import com.letsrace.game.FRConstants;
import com.letsrace.game.FRGameRenderer;
import com.letsrace.game.FRGameWorld;
import com.letsrace.game.GameWorld;
import com.letsrace.game.LetsRace;
import com.letsrace.game.input.FRCarKeyboardInputHandler;
import com.letsrace.game.input.FRInputAdapter;
import com.letsrace.game.input.FRNetworkInputHandler;

public class FRGameScreen extends ScreenAdapter{
	public LetsRace gameRef;
	public FRGameRenderer renderer;
	public FRGameWorld gameWorldRef;
	public FRInputAdapter adapter;

	public FRGameScreen(LetsRace letsRace, boolean multiplayer) {
		if(multiplayer){
			Gdx.app.log(FRConstants.TAG, "GameScreen(): Constructor");
			this.gameRef = letsRace;
			gameWorldRef = gameRef.client.gameWorld;
			gameRef.stage.clear();
			renderer = new FRGameRenderer(gameRef.myPlayerNo, gameWorldRef, gameRef.stage.getBatch());
			adapter = new FRNetworkInputHandler(gameRef.googleServices, gameRef.client.serverID);
			Gdx.input.setInputProcessor(adapter);
		}else{
			Gdx.app.log(FRConstants.TAG, "GameScreen(): Constructor");
			this.gameRef = letsRace;
			gameWorldRef = new FRGameWorld();
			gameWorldRef.carHandler.addCar(0, 0);
			renderer = new FRGameRenderer(0, gameWorldRef, gameRef.batch);
			adapter = new FRCarKeyboardInputHandler(gameWorldRef.carHandler.cars.get(0));
			Gdx.input.setInputProcessor(adapter);
		}
	}
	
	@Override
	public void render(float delta){
		adapter.handleAccelerometer();
		update(delta);
		draw();
	}
	
	private void draw() {
		GL20 gl = Gdx.gl;
		gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		renderer.render();
	}

	private void update(float delta) {
		gameWorldRef.update(delta);
	}

}
<file_sep>package com.letsrace.game.screen;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.letsrace.game.FRConstants;
import com.letsrace.game.FRConstants.GameState;
import com.letsrace.game.LetsRace;
import com.letsrace.game.network.FRMessageCodes;

public class FRCarSelectScreen extends ScreenAdapter {
	private LetsRace gameRef;

	public FRCarSelectScreen(LetsRace game){
		Gdx.app.log(FRConstants.TAG, "Car-Select: Constructor");
		gameRef = game;
		gameRef.stage.clear();
		Gdx.input.setInputProcessor(gameRef.stage);
		Image image = new Image(gameRef.skin.getDrawable("background"));
		image.setWidth(Gdx.graphics.getWidth());
		image.setHeight(Gdx.graphics.getHeight());
		Image name = new Image(gameRef.skin.getDrawable("car-selection-name"));
		name.setWidth(400*FRConstants.GUI_SCALE_WIDTH);
		name.setHeight(50*FRConstants.GUI_SCALE_WIDTH);
		name.setPosition(36*FRConstants.GUI_SCALE_WIDTH, (FRConstants.GUI_HIEGHT-30-50)*FRConstants.GUI_SCALE_WIDTH);
		Image character = new Image(gameRef.skin.getDrawable("car-selection-char"));
		character.setWidth(230*FRConstants.GUI_SCALE_WIDTH);
		character.setHeight(250*FRConstants.GUI_SCALE_WIDTH);
		character.setPosition(36*FRConstants.GUI_SCALE_WIDTH, (FRConstants.GUI_HIEGHT-96-250)*FRConstants.GUI_SCALE_WIDTH);
		Image stats = new Image(gameRef.skin.getDrawable("car-selection-stats"));
		stats.setWidth(150*FRConstants.GUI_SCALE_WIDTH);
		stats.setHeight(250*FRConstants.GUI_SCALE_WIDTH);
		stats.setPosition(286*FRConstants.GUI_SCALE_WIDTH, (FRConstants.GUI_HIEGHT-96-250)*FRConstants.GUI_SCALE_WIDTH);
		Image car = new Image(gameRef.skin.getDrawable("car-selection-car"));
		car.setWidth(400*FRConstants.GUI_SCALE_WIDTH);
		car.setHeight(400*FRConstants.GUI_SCALE_WIDTH);
		car.setPosition(36*FRConstants.GUI_SCALE_WIDTH, (FRConstants.GUI_HIEGHT-364-400)*FRConstants.GUI_SCALE_WIDTH);
		car.addListener(new ClickListener(){
			@Override
			public void clicked(InputEvent event, float x, float y) {
				super.clicked(event, x, y);
				byte[] msg = new byte[1];
				if (gameRef.myPlayerNo == 0)
					msg[0] = FRMessageCodes.SELECTED_CAR_0;
				else if (gameRef.myPlayerNo == 1)
					msg[0] = FRMessageCodes.SELECTED_CAR_1;
				gameRef.googleServices.sendReliableMessage(msg, gameRef.client.serverID);
				gameRef.moveToScreen(GameState.WAIT);
			}
		});
		gameRef.stage.addActor(image);
		gameRef.stage.addActor(name);
		gameRef.stage.addActor(character);
		gameRef.stage.addActor(stats);
		gameRef.stage.addActor(car);
	}

	public void show() {
		Gdx.app.log(FRConstants.TAG, "Car-Select: Show()");
		// Throwing stuff in constructor as loading stuff in show() causes black
		// screen
	}

	@Override
	public void render(float delta) {
		GL20 gl = Gdx.gl;
		gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		gameRef.stage.draw();
	}

	public void dispose() {
		Gdx.app.log(FRConstants.TAG, "Car-Select: Dispose()");
	}

}
