Presently I'm trying to implement simple game using libgdx for Android Platform. I have implemented the game but don't know how to pause and resume game based on user input.Kindly suggest idea as well as some practical code to implement the same.I am using simple game code demonstrated in libgdx library. Thank you.
Here is the code :
public class Drop implements ApplicationListener {
Texture dropImage;
Texture bucketImage;
Sound dropSound;
Music rainMusic;
SpriteBatch batch;
OrthographicCamera camera;
Rectangle bucket;
Array<Rectangle> raindrops;
long lastDropTime;
#Override
public void create() {
// load the images for the droplet and the bucket, 64x64 pixels each
dropImage = new Texture(Gdx.files.internal("droplet.png"));
bucketImage = new Texture(Gdx.files.internal("bucket.png"));
// load the drop sound effect and the rain background "music"
dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));
// start the playback of the background music immediately
rainMusic.setLooping(true);
rainMusic.play();
// create the camera and the SpriteBatch
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
batch = new SpriteBatch();
// create a Rectangle to logically represent the bucket
bucket = new Rectangle();
bucket.x = 800 / 2 - 64 / 2; // center the bucket horizontally
bucket.y = 20; // bottom left corner of the bucket is 20 pixels above the bottom screen edge
bucket.width = 64;
bucket.height = 64;
// create the raindrops array and spawn the first raindrop
raindrops = new Array<Rectangle>();
spawnRaindrop();
}
private void spawnRaindrop() {
Rectangle raindrop = new Rectangle();
raindrop.x = MathUtils.random(0, 800-64);
raindrop.y = 480;
raindrop.width = 64;
raindrop.height = 64;
raindrops.add(raindrop);
lastDropTime = TimeUtils.nanoTime();
}
#Override
public void render() {
// clear the screen with a dark blue color. The
// arguments to glClearColor are the red, green
// blue and alpha component in the range [0,1]
// of the color to be used to clear the screen.
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// tell the camera to update its matrices.
camera.update();
// tell the SpriteBatch to render in the
// coordinate system specified by the camera.
batch.setProjectionMatrix(camera.combined);
// begin a new batch and draw the bucket and
// all drops
batch.begin();
batch.draw(bucketImage, bucket.x, bucket.y);
for(Rectangle raindrop: raindrops) {
batch.draw(dropImage, raindrop.x, raindrop.y);
}
batch.end();
// process user input
if(Gdx.input.isTouched()) {
Vector3 touchPos = new Vector3();
touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos);
bucket.x = touchPos.x - 64 / 2;
}
if(Gdx.input.isKeyPressed(Keys.LEFT)) bucket.x -= 200 * Gdx.graphics.getDeltaTime();
if(Gdx.input.isKeyPressed(Keys.RIGHT)) bucket.x += 200 * Gdx.graphics.getDeltaTime();
// make sure the bucket stays within the screen bounds
if(bucket.x < 0) bucket.x = 0;
if(bucket.x > 800 - 64) bucket.x = 800 - 64;
// check if we need to create a new raindrop
if(TimeUtils.nanoTime() - lastDropTime > 1000000000) spawnRaindrop();
// move the raindrops, remove any that are beneath the bottom edge of
// the screen or that hit the bucket. In the later case we play back
// a sound effect as well.
Iterator<Rectangle> iter = raindrops.iterator();
while(iter.hasNext()) {
Rectangle raindrop = iter.next();
raindrop.y -= 200 * Gdx.graphics.getDeltaTime();
if(raindrop.y + 64 < 0) iter.remove();
if(raindrop.overlaps(bucket)) {
dropSound.play();
iter.remove();
}
}
}
#Override
public void dispose() {
// dispose of all the native resources
dropImage.dispose();
bucketImage.dispose();
dropSound.dispose();
rainMusic.dispose();
batch.dispose();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}
The most simple thing is, that you add an Enum:
public enum State
{
PAUSE,
RUN,
RESUME,
STOPPED
}
And modify your rendermethod
private State state = State.RUN;
#Override
public void render()
{
switch (state)
{
case RUN:
//do suff here
break;
case PAUSE:
//do stuff here
break;
case RESUME:
break;
default:
break;
}
}
Use the callback from android too. For example use the callbacks from libgdx to change the state:
#Override
public void pause()
{
this.state = State.PAUSE;
}
#Override
public void resume()
{
this.state = State.RESUME;
}
Also add something like a setMethod for the state so you can change it on userevent.
public void setGameState(State s){
this.state = s;
}
You can use states, with enums or constants. and only update in the Running state. Something like this:
public enum State{
Running, Paused
}
State state = State.Running;
#Override
public void render(){
switch(state){
case Running:
update();
break;
case Paused:
//don't update
break;
}
draw();
}
public void update(){
//your update code
}
public void draw(){
//your render code
}
I don't have the rep to reply to comments, but the flickering of items is something I ran into before.
You are no longer clearing the screen and redrawing the sprites in your render when its paused right? If I am not mistaken if you glClear and redraw the srpites in every render i think it will stop
Even more simple, see here: https://github.com/libgdx/libgdx/wiki/Continuous-&-non-continuous-rendering
In your ApplicationListener's create method, just put
Gdx.graphics.setContinuousRendering(false);
Gdx.graphics.requestRendering();
Set up a boolean
public boolean paused = false;
Then for example a button listener to pause/resume on a button press
buttonPause.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) {
// set paused true or false here
}
});
Then right at the end of your render method, just add:
if (!paused) {
Gdx.graphics.requestRendering();
}
The render method is now paused/resumed.
I tried using this approach:
I made GAME_PAUSED a boolean , If it is true don't update your screen or camera . Otherwise update Camera .
boolean GAME_PAUSED = false;
if (GAME_PAUSED) {
//Do not update camera
batch.begin();
resumeButton.draw(batch);
batch.end();
} else {
//Update Camera
Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
world.step(1/60f, 8, 3);
camera.update();
debugRenderer.render(world, camera.combined);
//Do your game running
}
Related
so as my previous threads show, I am creating a gameObject from sprites images at runtime using this code:
tex = Resources.Load<Texture2D>("pig") as Texture2D;
Sprite sprite = new Sprite();
sprite = Sprite.Create(tex, new Rect(0, 0, 250, 150), new Vector2(0.5f, 0.5f));
GameObject newSprite = new GameObject();
newSprite.AddComponent<Rigidbody2D>();
newSprite.GetComponent<Rigidbody2D>().gravityScale = 0f;
newSprite.AddComponent<ObjectMovement>();
newSprite.AddComponent<SpriteRenderer>();
SR = newSprite.GetComponent<SpriteRenderer>();
SR.sprite = sprite;
As you see I added a script "ObjectMovement", I want to check in this script if someone is dragging this particular gameObject and if so, make it follow the touch position, just to mention - this game is 2D. I never used RaysorRaycast so I am not sure where I gone wrong. Anyway here is my script code:
public SpriteRenderer selection=null;
void Update()
{
if (Input.touchCount >= 1)
{
foreach (Touch touch in Input.touches)
{
Ray ray = Camera.main.ScreenPointToRay(touch.position);
RaycastHit hit;
switch (touch.phase)
{
case TouchPhase.Began:
if (Physics.Raycast(ray, out hit, 100))
selection = hit.transform.gameObject.GetComponent<SpriteRenderer>();
break;
case TouchPhase.Moved:
selection.transform.position = new Vector2(selection.transform.position.x + touch.position.x / 10, selection.transform.position.y + touch.position.y / 10);
break;
case TouchPhase.Ended:
selection = null;
break;
}
}
}
}
So basically - when touching the screen, fire a ray and check which gameObject is in this position, when moving the finger make it follow it. Drag and drop. Thanks.
EDIT: I noticed the script is attached to every gameObject which is not effective, any ideas?
For 2D, you use RaycastHit2D and Physics2D.Raycast instead of RaycastHit and Physics.Raycast. Those are for 3D. Secondly, Make sure to attach a collider to the Sprite. Since this is a 2D game, the collider must have word "2D" in it. For example, Box Colider 2D from the Editor. You can also use Circle Collider 2D.
I noticed the script is attached to every gameObject which is not
effective, any ideas?
Just create an empty GameObject and attach that script to it. That's it.
Here is fixed version of your code:
float tempZAxis;
public SpriteRenderer selection;
void Update()
{
Touch[] touch = Input.touches;
for (int i = 0; i < touch.Length; i++)
{
Vector2 ray = Camera.main.ScreenToWorldPoint(Input.GetTouch(i).position);
RaycastHit2D hit = Physics2D.Raycast(ray, Vector2.zero);
switch (touch[i].phase)
{
case TouchPhase.Began:
if (hit)
{
selection = hit.transform.gameObject.GetComponent<SpriteRenderer>();
if (selection != null)
{
tempZAxis = selection.transform.position.z;
}
}
break;
case TouchPhase.Moved:
Vector3 tempVec = Camera.main.ScreenToWorldPoint(touch[i].position);
tempVec.z = tempZAxis; //Make sure that the z zxis never change
if (selection != null)
{
selection.transform.position = tempVec;
}
break;
case TouchPhase.Ended:
selection = null;
break;
}
}
}
That would only work on mobile but not on the Desktop Build. I suggest you implement IBeginDragHandler, IDragHandler, IEndDragHandler and override the functions that comes with them. Now, it will work with both mobile and desktop platforms.
Note: For the second solution you have to attach the script below to all Sprites you want to drag unlike the first script above.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class Dragger : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
Camera mainCamera;
float zAxis = 0;
Vector3 clickOffset = Vector3.zero;
// Use this for initialization
void Start()
{
//Comment this Section if EventSystem system is already in the Scene
addEventSystem();
mainCamera = Camera.main;
mainCamera.gameObject.AddComponent<Physics2DRaycaster>();
zAxis = transform.position.z;
}
public void OnBeginDrag(PointerEventData eventData)
{
clickOffset = transform.position - mainCamera.ScreenToWorldPoint(new Vector3(eventData.position.x, eventData.position.y, zAxis));
}
public void OnDrag(PointerEventData eventData)
{
//Use Offset To Prevent Sprite from Jumping to where the finger is
Vector3 tempVec = mainCamera.ScreenToWorldPoint(eventData.position) + clickOffset;
tempVec.z = zAxis; //Make sure that the z zxis never change
transform.position = tempVec;
}
public void OnEndDrag(PointerEventData eventData)
{
}
//Add Event Syste to the Camera
void addEventSystem()
{
GameObject eventSystem = new GameObject("EventSystem");
eventSystem.AddComponent<EventSystem>();
eventSystem.AddComponent<StandaloneInputModule>();
}
}
OK for sake of argument and simplicity this code here has a rectangle sprite/texture that shoots(cuz it's a gun) upwards. And an enemy rectangle/sprite/texture the spawns downwards. Then the player detects if it hits a enemy. When the player hits an enemy I get an out of bounds exception -1
package com.TheGame.Pack;
import java.util.Iterator;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.TimeUtils;
public class GameScreen implements Screen {
final MasterClass game;
Texture FleetTexture;
Texture PlayerTexture;
Texture ShootingTexture;
OrthographicCamera camera;
Rectangle Player;
Array<Rectangle> Emma;
Array<Rectangle> Shooting;
long EmmaSpawnTime;
long ShootingTime;
public static int EmmaKilled = 0;
public GameScreen(final MasterClass gam) {
this.game = gam;
// load the images for the droplet and the Player, 64x64 pixels each
FleetTexture = new Texture(Gdx.files.internal("cirA.png")); //Enemies
PlayerTexture = new Texture(Gdx.files.internal("BoxA.png"));
ShootingTexture = new Texture(Gdx.files.internal("gun.png"));
// load the drop sound effect and the rain background "music"
// dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
// rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));
// rainMusic.setLooping(true);
// create the camera and the SpriteBatch
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
// create a Rectangle to logically represent the Player
Player = new Rectangle();
Player.x = 800 / 2 - 64 / 2; // center the Player horizontally
Player.y = 20; // bottom left corner of the Player is 20 pixels above
// the bottom screen edge
Player.width = 40;
Player.height = 30;
// create the Emma array and spawn the first EmmaInArray
Emma = new Array<Rectangle>();
Shooting = new Array<Rectangle>();
spawnEmma();
}
private void spawnEmma() {
Rectangle EmmaInArray = new Rectangle();
EmmaInArray.x = MathUtils.random(0, 800 - 64);
EmmaInArray.y = 480;
EmmaInArray.width = 40;
EmmaInArray.height = 30;
Emma.add(EmmaInArray);
EmmaSpawnTime = TimeUtils.nanoTime();
}
private void spawnShooting(){
Rectangle ShootingInArray = new Rectangle();
ShootingInArray.x = Player.x;
ShootingInArray.y = Player.y;
ShootingInArray.width = 40;
ShootingInArray.height = 30;
Shooting.add(ShootingInArray);
ShootingTime = TimeUtils.nanoTime();
}
#Override
public void render(float delta) {
// clear the screen with a dark blue color. The
// arguments to glClearColor are the red, green
// blue and alpha component in the range [0,1]
// of the color to be used to clear the screen.
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// tell the camera to update its matrices.
camera.update();
// tell the SpriteBatch to render in the
// coordinate system specified by the camera.
game.batch.setProjectionMatrix(camera.combined);
// begin a new batch and draw the Player and
// all drops
game.batch.begin();
game.font.draw(game.batch, "Drops Collected: " + EmmaKilled, 0, 480);
game.batch.draw(PlayerTexture, Player.x, Player.y, Gdx.graphics.getWidth() / 20,
Gdx.graphics.getHeight()/ 20);
for (Rectangle EmmaInArray : Emma) {
game.batch.draw(FleetTexture, EmmaInArray.x, EmmaInArray.y);
}
for(Rectangle ShootingInArray : Shooting){
game.batch.draw(ShootingTexture, ShootingInArray.x, ShootingInArray.y);
ShootingInArray.y +=10;
}
game.batch.end();
// process user input
if (Gdx.input.isTouched()) {
Vector3 touchPos = new Vector3();
touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos);
Player.x = touchPos.x - 64 / 2;
}
if (Gdx.input.isKeyPressed(Keys.LEFT))
Player.x -= 400 * Gdx.graphics.getDeltaTime();
if (Gdx.input.isKeyPressed(Keys.RIGHT))
Player.x += 400 * Gdx.graphics.getDeltaTime();
// make sure the Player stays within the screen bounds
if (Player.x < 0)
Player.x = 0;
if (Player.x > 800 - 64)
Player.x = 800 - 64;
// check if we need to create a new EmmaInArray
if (TimeUtils.nanoTime() - EmmaSpawnTime > 100000000){
spawnEmma();
}
if(TimeUtils.nanoTime() - ShootingTime > 100000000){
spawnShooting();
}
// move the Emma, remove any that are beneath the bottom edge of
// the screen or that hit the Player. In the later case we play back
// a sound effect as well.
Iterator<Rectangle> EmmaIterator = Emma.iterator();
while (EmmaIterator.hasNext()) {
Rectangle EmmaInArray = EmmaIterator.next();
EmmaInArray.y -= 200 * Gdx.graphics.getDeltaTime();
if (EmmaInArray.y + 64 < 0){
EmmaIterator.remove();
}
Iterator<Rectangle> ShootingIterator = Shooting.iterator();
while(ShootingIterator.hasNext()){
Rectangle ShootingInArray = ShootingIterator.next();
// ShootingInArray.y += 200 * Gdx.graphics.getDeltaTime();
if(ShootingInArray.y > 480){
ShootingIterator.remove();
}
if(EmmaInArray.overlaps(ShootingInArray)){
ShootingIterator.remove();
EmmaIterator.remove();
}
if (Player.overlaps(EmmaInArray)) {
EmmaKilled++;
game.setScreen(game.HS);
// dropSound.play();
if I comment out EmmaIterator.remove(); it runs fine with it uncommented it crashes upon hit.
Why does this crash is this not the proper way to do this? Do I need to somehow detect hit's at the same time? How can the array be at negative 1 when clearly there are still enemies on the screen?
EmmaIterator.remove();
}
}
Though this is not the way I will have things setup this code still should run with no issues. I encounter the same problem when instead of detecting the player enemies collisions I have 2 guns checking for collisions. This seems like a big problem to me which is why I'd say I'm just doing it wrong but documentation is light so I come here.
}
}
#Override
public void resize(int width, int height) {
}
#Override
public void show() {
// start the playback of the background music
// when the screen is shown
//rainMusic.play();
}
#Override
public void hide() {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void dispose() {
FleetTexture.dispose();
PlayerTexture.dispose();
ShootingTexture.dispose();
// dropSound.dispose();
// rainMusic.dispose();
}
}
It's unlikely, unless multithreading is being used, that anything will happen at the same time exactly. You have a number of probable typos in your code, but one will break it:
Rectangle var1_holder = iter.next();
That same reference to iter is in both the first block, which should use it, and the second block, which should use iter1. You should consider using matching variable names, like
Iterator<Rectangle> iter3 = var3.iterator();
if you must have numbers as the only distinguishing feature.
notostraca is right. But to make it more clear i will show u an example with for loops which i use for collisions. it can't make any harm and i hope it will make it more clear for u
int v2 = var2.size();
for (int i = 0; i < v2; i++) {
if (object.getBounds().overlaps(var2.get(i).getBounds())) {
var2.remove(i);
v2--;
//in this line u might use break; if u know that just one object
//from var2 array can hit at a time
}
}
I'm new to game development andengine. I want to add ParallaxBackground but I don't know how to change background on player move. I'm using arrow for moving a player. Now my question is where I write the code parallaxBackground.setParallaxValue(5); I was written this line in onAreaTouched method of arrow but it not work. please help me. Thanks.
Code
private Camera mCamera;
private static int CAMERA_WIDTH = 800;
private static int CAMERA_HEIGHT = 480;
private BitmapTextureAtlas bgTexture;
private ITextureRegion bgTextureRegion;
#Override
protected void onCreateResources() {
BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
bgTexture = new BitmapTextureAtlas(getTextureManager(),2160,480,TextureOptions.REPEATING_BILINEAR);
bgTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(bgTexture, this, "background.png", 0, 0);
bgTexture.load();
}
#Override
protected Scene onCreateScene() {
this.getEngine().registerUpdateHandler(new FPSLogger());
Scene scene = new Scene();
scene.setBackground(new Background(Color.BLACK));
final ParallaxBackground parallaxBackground = new ParallaxBackground(0, 0, 0);
final VertexBufferObjectManager vertexBufferObjectManager = this.getVertexBufferObjectManager();
parallaxBackground.attachParallaxEntity(new ParallaxEntity(0.0f, new Sprite(0, CAMERA_HEIGHT - this.bgTextureRegion.getHeight(), this.bgTextureRegion, vertexBufferObjectManager)));
scene.setBackground(parallaxBackground);
Robot robot = new Robot();
// add Player
final AnimatedSprite animatedRobotSprite = new AnimatedSprite(robot.centerX, robot.centerY, 122, 126, (ITiledTextureRegion) robotTextureRegion, getVertexBufferObjectManager());
scene.attachChild(animatedRobotSprite);
animatedRobotSprite.animate(new long[]{1250,50,50});
// add right arrow button
Sprite rightArrowSprite = new Sprite(0, CAMERA_HEIGHT-70, rightArrowTextureRegion, getVertexBufferObjectManager()){
#Override
public boolean onAreaTouched(TouchEvent pSceneTouchEvent,float pTouchAreaLocalX, float pTouchAreaLocalY) {
switch (pSceneTouchEvent.getAction()) {
case TouchEvent.ACTION_DOWN:
moveRight = true;
parallaxBackground.setParallaxValue(5);
break;
case TouchEvent.ACTION_MOVE:
moveRight = true;
break;
case TouchEvent.ACTION_UP:
moveRight = false;
break;
default:
break;
}
return super.onAreaTouched(pSceneTouchEvent, pTouchAreaLocalX, pTouchAreaLocalY);
}
};
scene.attachChild(rightArrowSprite);
scene.registerTouchArea(rightArrowSprite);
scene.setTouchAreaBindingOnActionDownEnabled(true);
scene.setTouchAreaBindingOnActionMoveEnabled(true);
scene.registerUpdateHandler(new IUpdateHandler() {
#Override
public void reset() {
}
#Override
public void onUpdate(float pSecondsElapsed) {
if ( moveRight ){
animatedRobotSprite.setPosition(animatedRobotSprite.getX()+speedX, animatedRobotSprite.getY());
}
}
});
return scene;
}
I see at least on possible problem: You have only one ParallaxEntity attached. The visual effect of parallax is created by multiple entities moving at different speeds.
But I think what you are seeing is that your background is not scrolling. If you do not use the AutoParallaxBackground class, you have to update the paralax amount on each update.
Inside the autoparallax class, this is what it does onUpdate():
#Override
public void onUpdate(final float pSecondsElapsed) {
super.onUpdate(pSecondsElapsed);
this.mParallaxValue += this.mParallaxChangePerSecond * pSecondsElapsed;
}
Looking at this you can see that the setParallaxValue() is an absolute number, so to make it continually scroll, you will need to feed it a new number on each update. For example, in you main game update loop, you could feed in your player.getX() into parallaxBackground.setParallaxValue() like so:
parallaxBackground.setParallaxValue(rightArrowSprite.getX());
While this may not be the exact effect you want, you should now see the background moving when your character moves.
There are different modifiers foe updating your entity(AnimatedSprite) you can use them for movement like
final Entity playerEntity = ...;//Get player entity here.
final float jumpDuration = 2;
final float x= playerEntity.getX();
final MoveXModifiermoveModifier = new MoveXModifier(jumpDuration / 2, x+ 100, y);
playerEntity.registerEntityModifier(moveModifier );
It's not necessary to have two background moving at different speeds. With one fixed background and one "moving" background you will notice the effect when the player moves. However, I've tried setting an absolute value in
parallaxBackground.setParallaxValue(10f);
And the effect was not continuous. What it worked for me was a recipe found in Android Cookbook which is based on overriding the onUpdate of the background to change only when the camera has moved.
ParallaxBackground background = new ParallaxBackground(0.3f, 0.3f,0.9f) {
/* We'll use these values to calculate the parallax value of the background*/
float cameraPreviousX = 0;
float parallaxValueOffset = 0;
/* onUpdates to the background, we need to calculate new
* parallax values in order to apply movement to the background
* objects (the hills in this case) */
#Override
public void onUpdate(float pSecondsElapsed) {
/* Obtain the camera's current center X value */
final float cameraCurrentX = mCamera.getCenterX();
/* If the camera's position has changed since last update... */
if (cameraPreviousX != cameraCurrentX) {
/* Calculate the new parallax value offset by subtracting the previous update's camera x coordinate from the current update's camera x coordinate */
parallaxValueOffset += cameraCurrentX - cameraPreviousX;
/* Apply the parallax value offset to the background, which will in-turn offset the positions of entities attached to the background */
this.setParallaxValue(parallaxValueOffset);
/* Update the previous camera X since we're finished with this update */
cameraPreviousX = cameraCurrentX;
}
super.onUpdate(pSecondsElapsed);
}
};
background.attachParallaxEntity(new ParallaxEntity(5, hillFurthest));
Note than depending on the camera configuration, the "5" value included in the "attachParallaxEntity" may need to be negative. Otherwise, the background will move in the opposite direction of the player.
I am in the process of learning cocos2d-android. I have been following a tutorial that ported some of Ray Wenderlich's iOS tutorials to Android. I have the first tutorials finished and wanted to continue on by converting the next Ray Wenderlich tutorial to Android by myself as an exercise in learning.
The original iOS tutorial can be found here http:// www raywenderlich com/1163/how-to-make-a-tile-based-game-with-cocos2d
I have converted the application to android but am having some trouble with the way its behaving.
My code is here:
public class GameLayer extends CCLayer{
private CGSize _winSize;
protected ArrayList<CCSprite> _targets;
protected ArrayList<CCSprite> _projectiles;
protected int _projectilesDestroyed;
protected CCSprite _player;
protected CCSprite _nextProjectile;
protected CCTMXTiledMap _map;
protected CCTMXLayer _background;
protected CCTMXObjectGroup _objects;
protected HashMap<String, String> _spawnPoint;
protected GameLayer() {
super();
_winSize = CCDirector.sharedDirector().displaySize();
_targets = new ArrayList<CCSprite>();
_projectiles = new ArrayList<CCSprite>();
_projectilesDestroyed = 0;
// Get TMX Map and associated layers/groups
_map = CCTMXTiledMap.tiledMap("TileMap.tmx");
_background = _map.layerNamed("Background");
_objects = _map.objectGroupNamed("Objects");
// Add my background layer
// TODO: Position layer in the correct spot.
addChild(_background);
_spawnPoint = _objects.objectNamed("SpawnPoint");
_player = CCSprite.sprite("Player3.png");
setPlayerPosition(CGPoint.ccp (100.0f, 100.0f));
addChild(_player);
setViewPointCentered(_player.getPosition());
Context context = CCDirector.sharedDirector().getActivity();
SoundEngine.sharedEngine().preloadEffect(context, R.raw.pew_pew_lei);
SoundEngine.sharedEngine().playSound(context, R.raw.background_music_aac, true);
this.setIsTouchEnabled(true);
this.schedule("update");
}
public void setViewPointCentered(CGPoint pos) {
float x = 0.0f;
float y = 0.0f;
x = Math.max(pos.x, _winSize.width / 2);
y = Math.max(pos.y, _winSize.height / 2);
x = Math.min(x, (_map.getMapSize().width * _map.getTileSize().width) - _winSize.width / 2 );
y = Math.min(y, (_map.getMapSize().height * _map.getTileSize().height) - _winSize.height / 2);
CGPoint actualPos = CGPoint.ccp(x, y);
CGPoint centerOfView = CGPoint.ccp(_winSize.width / 2, _winSize.height / 2);
CGPoint viewPoint = CGPoint.ccpSub(centerOfView, actualPos);
_background.setPosition(viewPoint);
}
public static CCScene scene() {
CCScene scene = CCScene.node();
CCLayer layer = new GameLayer();
scene.addChild(layer);
return scene;
}
#Override
public boolean ccTouchesBegan(MotionEvent event) {
return true;
}
void setPlayerPosition(CGPoint position) {
_player.setPosition(position);
}
#Override
public boolean ccTouchesEnded(MotionEvent event) {
// Choose one of the touches to work with
CGPoint touchLocation = CGPoint.ccp(event.getX(), event.getY());
touchLocation = CCDirector.sharedDirector().convertToGL(touchLocation);
touchLocation = this.convertToNodeSpace(touchLocation);
CGPoint playerPosition = _player.getPosition();
CGPoint diff = CGPoint.ccpSub(touchLocation, playerPosition);
if (Math.abs(diff.x) > Math.abs(diff.y)) {
if (diff.x > 0) {
playerPosition.x += _map.getTileSize().width;
} else {
playerPosition.x -= _map.getTileSize().width;
}
} else {
if (diff.y > 0) {
playerPosition.y += _map.getTileSize().height;
} else {
playerPosition.y -= _map.getTileSize().height;
}
}
if (playerPosition.x <= (_map.getMapSize().width * _map.getTileSize().width) &&
playerPosition.y <= (_map.getMapSize().height * _map.getTileSize().height) &&
playerPosition.y >= 0 &&
playerPosition.x >= 0 ) {
setPlayerPosition(playerPosition);
}
setViewPointCentered(_player.getPosition());
return true;
}
public void finishShoot() {
addChild(_nextProjectile);
_projectiles.add(_nextProjectile);
}
public void update(float dt) {
ArrayList<CCSprite> projectilesToDelete = new ArrayList<CCSprite>();
for (CCSprite projectile : _projectiles) {
CGRect projectileRect = CGRect.make(projectile.getPosition().x - (projectile.getContentSize().width / 2.0f),
projectile.getPosition().y - (projectile.getContentSize().height / 2.0f),
projectile.getContentSize().width,
projectile.getContentSize().height);
ArrayList<CCSprite> targetsToDelete = new ArrayList<CCSprite>();
for (CCSprite target : _targets) {
CGRect targetRect = CGRect.make(target.getPosition().x - (target.getContentSize().width),
target.getPosition().y - (target.getContentSize().height),
target.getContentSize().width,
target.getContentSize().height);
if (CGRect.intersects(projectileRect, targetRect)) {
targetsToDelete.add(target);
}
}
for (CCSprite target : targetsToDelete) {
_targets.remove(target);
removeChild(target, true);
}
if (targetsToDelete.size() > 0) {
projectilesToDelete.add(projectile);
}
}
for (CCSprite projectile : projectilesToDelete) {
_projectiles.remove(projectile);
removeChild(projectile, true);
if (++_projectilesDestroyed > 30) {
_projectilesDestroyed = 0;
CCDirector.sharedDirector().replaceScene(GameOverLayer.scene("You Win!"));
}
}
}
}
I first grab my display size and create my tiled map from my TMX file. I get my background layer and add it as a child. I then grab my objects layer and pull my spawn point object out of the map (I override this spawn point with 100, 100 for testing purposes). I grab my player sprite and set my player position to the 100, 100 coordinates. I then add the player as a child.
Next I call setViewPointCentered to move my map to an appropriate position based on my players position. This part works just fine and my map gets placed with the lower left corner (0,0) placed in the lower left corner (0,0) of my screen and my character is at 100,100 slightly left and down from the center of the screen.
The problem occurs when I begin moving up or right. Once I pass the center of the screen I would expect to have the player sprite stay centered on the screen and the background move the opposite direction as I continue moving. However both the player and the background move so eventually my player comes to the right or top edge of the screen and I can't move up or right any longer even though there is plenty of map left.
Notice the player in the upper left corner of the map.
Player reaching the top of screen and not staying centered as expected
Notice the player in the lower right corner of the map.
Player reaching the right of screen and not staying centered as expected
The "public boolean ccTouchesEnded(MotionEvent event)" method and the "public void setViewPointCentered(CGPoint pos)" method handle the player and view positioning but I don't think they're working correctly.
A friend of mine does iOS programming and created the app on his iPhone and it's working as expected so I'm wondering if there is a bug in the android port of cocos2d.
Does anyone have any ideas on why the character won't stay centered on the screen when I get to the middle and continue moving right or up on the map?
Thanks for any input you can provide. I've been beating my head against my desk for two days trying to figure this out.
OK I figured it out.
In this piece of code:
// Get TMX Map and associated layers/groups
_map = CCTMXTiledMap.tiledMap("TileMap.tmx");
_background = _map.layerNamed("Background");
_objects = _map.objectGroupNamed("Objects");
// Add my background layer
// TODO: Position layer in the correct spot.
addChild(_background);
I am adding my _background layer but what I really want to be doing is adding my _map instead:
// Get TMX Map and associated layers/groups
_map = CCTMXTiledMap.tiledMap("TileMap.tmx");
_background = _map.layerNamed("Background");
_objects = _map.objectGroupNamed("Objects");
// Add my background layer
// TODO: Position layer in the correct spot.
addChild(_map);
Now my player character remains in the center of my viewable screen while walking around unless I get to the edge of the map.
Each time when the touch ends, the player is moved by mapWidth or height. You should move the position with diff. Try
CGPoint diff = CGPoint.ccpSub(touchLocation, playerPosition);
if (Math.abs(diff.x) > Math.abs(diff.y)) {
if (diff.x > 0) {
playerPosition.x += diff.x;
} else {
playerPosition.x -= diff.x;
}
} else {
if (diff.y > 0) {
playerPosition.y += diff.y;
} else {
playerPosition.y -= diff.y;
}
}
I've written a game which uses a lot of lines, circles (some outlined, some filled, some with both) and text elements throughout to create a guitar fretboard which I get the user to interact with. Some of these elements are animated (coordinate, alpha, colour or a combination) and the app starts to skip lots of frames during most of the animations which I'd like to fix. I think OpenGL is the way to go, but I'm interested in some pointers before I jump in.
Currently my animation is achieved with lookup tables, async tasks and dynamic bitmap creation (for the text - I render it to bitmaps as I use custom fonts - so I never draw text directly to the canvas). I've got the async task running for n * 1000 ms and in the thread it waits for x ms (typically 50ms) and then pushes a progress message out - the helper classes then work out where in the time indexed lookup table the animation is and calculates the relative values based on that. I draw the static bits of the fretboard directly to the canvas with the included draw circle and draw line methods.
I'm not sure what is slowing my app down currently (mostly because I've not yet profiled it) but I'm pretty sure that even though I cache the bitmaps and have been fairly sensible about the way that I'm changing size & transparency, the use of bitmaps drawn directly to the Canvas is what is causing the slow down.
I've followed some tutorials and have written some OpenGL, but not enough to know much at all - I know it's fast though which is important. I don't know if I can use the same methods of drawing lines and circles directly to the canvas with OpenGL, and I think I'll still have to create some bitmaps for the text and I think I have to apply these as textures in order to show them.
So can anyone give me some pointers? Some sample code of drawing lines, circles and text would be amazing. Any pointers on animating within OpenGL - I think my current setup is pretty solid and can prob port it over but any advice would be great as this is my first look in to animating.
* EDIT *
Here's a basic overview of my code - there are many pieces, but I'm including them in the hope that someone else may be able to use some of it.:
I have a look up table
public enum LookUpTable {
COUNT_DOWN {
#Override
public float[][] getLookUpTable() {
/*
* 1 = total time left
* 2 = font alpha
* 3 = font size
*/
float[][] lookUpTable = {
{ 0f, 0f, 400f },
{ 400f, 255f, 200f },
{ 700f, 255f, 150f },
{ 1000f, 0f, 5f }
};
return lookUpTable;
}
#Override
public LookUpType getLookUpType() {
return LookUpType.REPEAT;
}
};
// does the timer loop around, or is it a one off run
private enum LookUpType {
REPEAT, SINGLE
}
abstract public LookUpType getLookUpType();
abstract public float[][] getLookUpTable();
}
I have extended the AsyncTask task into a builder function:
public class CountDownTimerBuilder {
// callbacks - instantiated in the view
protected CountDownEndEvent countDownEndEvent;
protected CountDownProgressEvent countDownProgressEvent;
protected CountDownInitEvent countDownInitEvent;
protected int updatePeriod;
protected float runTime;
public CountDownTimerBuilder withCountDownEndEvent(CountDownEndEvent countDownEndEvent) {
this.countDownEndEvent = countDownEndEvent;
return this;
}
public CountDownTimerBuilder withCountDownProgressEvent(CountDownProgressEvent countDownProgressEvent) {
this.countDownProgressEvent = countDownProgressEvent;
return this;
}
public CountDownTimerBuilder withCountDownInitEvent(CountDownInitEvent countDownInitEvent) {
this.countDownInitEvent = countDownInitEvent;
return this;
}
public CountDownTimerBuilder withUpdatePeriod(int updatePeriod) {
this.updatePeriod = updatePeriod;
return this;
}
public CountDownTimerBuilder withRunTime(float runTime) {
this.runTime = runTime;
return this;
}
public CountDownTimer build() {
return new CountDownTimer();
}
public static interface CountDownEndEvent {
public abstract void dispatch(Long... endResult);
}
public static interface CountDownInitEvent {
public abstract void dispatch();
}
public static interface CountDownProgressEvent {
public abstract void dispatch(Long... progress);
}
public class CountDownTimer {
AsyncTask<Void, Long, Long> genericTimerTask;
/**
* Starts the internal timer
*/
public void start() {
genericTimerTask = new GenericCountDownTimer().execute(new Void[] {});
}
public void cancel() {
if (genericTimerTask != null) {
genericTimerTask.cancel(true);
genericTimerTask = null;
}
}
private class GenericCountDownTimer extends AsyncTask<Void, Long, Long> {
#Override
protected Long doInBackground(Void... params) {
long startTime = System.currentTimeMillis();
long currentTime;
long countDown;
Log.i(ApplicationState.getLogTag(getClass()), "Timer running for " + runTime + " ms, updating every " + updatePeriod + " ms");
do {
try {
Thread.sleep(updatePeriod);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (this.isCancelled()) {
Log.i(ApplicationState.getLogTag(getClass()), "Timer Cancelled");
break;
}
currentTime = System.currentTimeMillis();
countDown = currentTime - startTime;
publishProgress((long)runTime - countDown);
} while (countDown <= runTime);
return 0l;
}
#Override
protected void onPreExecute() {
if (countDownInitEvent != null) {
countDownInitEvent.dispatch();
}
}
#Override
protected void onProgressUpdate(Long... progress) {
Log.v(ApplicationState.getLogTag(getClass()), "Timer progress " + progress[0] + " ms");
if (countDownProgressEvent != null) {
countDownProgressEvent.dispatch(progress);
}
}
#Override
protected void onPostExecute(Long endresult) {
if (countDownEndEvent != null) {
countDownEndEvent.dispatch(endresult);
}
}
}
}
}
I have a class where my animation values are calculated:
public class AnimationHelper {
private LookUpTable lookUpTable;
private float[][] lookUpTableData;
private float currentTime = -1;
private float multiplier;
private int sourceIndex;
public void setLookupTableData(LookUpTable lookUpTable) {
if (this.lookUpTable != lookUpTable) {
this.lookUpTableData = lookUpTable.getLookUpTable();
this.currentTime = -1;
this.multiplier = -1;
this.sourceIndex = -1;
}
}
private void setCurrentTime(float currentTime) {
this.currentTime = currentTime;
}
public float calculate(float currentTime, int index) {
if (this.currentTime == -1 || this.currentTime != currentTime) {
setCurrentTime(currentTime);
getCurrentLookupTableIndex();
getMultiplier();
}
return getCurrentValue(index);
}
private void getCurrentLookupTableIndex() {
sourceIndex = -1;
for (int scanTimeRange = 0; scanTimeRange < (lookUpTableData.length - 1); scanTimeRange++) {
if (currentTime < lookUpTableData[scanTimeRange + 1][0]) {
sourceIndex = scanTimeRange;
break;
}
}
}
private void getMultiplier() {
if ((lookUpTableData[sourceIndex][0] - lookUpTableData[sourceIndex + 1][0]) == 0.0f) {
multiplier = 0.0f;
} else {
multiplier = (currentTime - lookUpTableData[sourceIndex][0]) / (lookUpTableData[sourceIndex + 1][0] - lookUpTableData[sourceIndex][0]);
}
}
public float getCurrentValue(int index) {
float currentValue = lookUpTableData[sourceIndex][index] + ((lookUpTableData[sourceIndex + 1][index] - lookUpTableData[sourceIndex][index]) * multiplier);
return currentValue > 0 ? currentValue : 0;
}
}
In my game code I tie it all together by specifying the lookup table to use and creating callbacks for each of the different states, creating the timer with the builder class and starting it:
AnimationHelper animHelper = new AnimationHelper();
animHelper.setLookupTableData(LookUpTable.COUNT_DOWN);
CountDownInitEvent animationInitEvent = new CountDownInitEvent() {
public void dispatch() {
genericTimerState = TimerState.NOT_STARTED;
}
};
CountDownProgressEvent animationProgressEvent = new CountDownProgressEvent() {
public void dispatch(Long... progress) {
genericTimerState = TimerState.IN_PROGRESS;
// update the generic timer - we'll use this in all animations
genericTimerCountDown = progress[0];
invalidate();
}
};
CountDownEndEvent animationEndEvent = new CountDownEndEvent() {
public void dispatch(Long... endValue) {
genericTimerState = TimerState.FINISHED;
startGame();
}
};
CountDownTimer timer = new CountDownTimerBuilder()
.withRunTime(getCountDownPeriod(countDownTimePeriod)) // getCountDownPeriod() is used for handling screen rotation - esentially returns the run time for the timer in ms
.withUpdatePeriod(TIMER_UPDATE_PERIOD) // currently set at 50
.withCountDownInitEvent(animationInitEvent)
.withCountDownProgressEvent(animationProgressEvent)
.withCountDownEndEvent(animationEndEvent)
.build();
timer.start();
in my onDraw I get the specific values from the lookup table and act on them:
private int IFTL = 0; // total time left
private int IFY1 = 1; // initial instructions y offset
private int IFY2 = 2; // start message y offset
private int IFA1 = 3; // note to guess alpha
float yPosition1 = animHelper.calculate(genericTimerCountDown, IFY1);
float yPosition2 = animHelper.calculate(genericTimerCountDown, IFY2);
float alpha1 = animHelper.calculate(genericTimerCountDown, IFA1);
// getScreenDrawData() returns the coordinates and other positioning info for the bitmap
final ScreenDrawData guessNoteTitleDrawValues = FretBoardDimensionHelper.getScreenDrawData(AssetId.GUESS_NOTE_TITLE);
//change the y position of the bitmap being drawn to screen
guessNoteTitleDrawValues.withAlteredCoordinate(Constants.Y_COORDINATE, 0-yPosition1);
//
DrawBitmapBuilder.createInstance()
.withCanvas(getCanvas())
.withBitmap(bitmapCacheGet(AssetId.GUESS_NOTE_TITLE))
.withBitmapDrawValues(guessNoteTitleDrawValues)
.draw();
Paint paint = new Paint();
paint.setAlpha((int)alpha1);
final ScreenDrawData initialNoteDrawValues = FretBoardDimensionHelper.getScreenDrawData(AssetId.GUESS_NOTE_INITIAL_NOTE);
// draw to screen with specified alpha
DrawBitmapBuilder
.createInstance()
.withCanvas(getCanvas())
.withBitmap(bitmapCacheGet(AssetId.GUESS_NOTE_INITIAL_NOTE))
.withBitmapDrawValues(initialNoteDrawValues)
.withPaint(paint)
.draw();
You'll have to read between the lines a bit in that last bit of code as there are a load of helper functions in there.
Does this look like a reasonable approach? I'd love a code review if anyone can be bothered - more than happy to post up more code or explanations if required
I've been working a bit with OpenGL and well... It's not trivial.
For the text, you'll have to create a mutable Bitmap, write some text in it with canvas.drawText(...), and then convert this Bitmap (which should be in powerOfTwo dimension) to a gl texture, and apply it to a rectangle.
For circles... it's gonna be complicated. OpenGL draws lines, triangles, dots... not circles i'm afraid. One way to achieve a circle in OpenGL is to have a texture of a circle, and apply it to a rectangle.
Each time your application is put on pause, and resumed, you'll have to recreate each of your textures, and set up your gl surface once again...
If you need blending, you'll probably find that, with a lot of textures, your phone doesn't have enough memory to handle it all...
Now that I have scared you off : open gl is really one of the way to go ! It'll allow your app to delegate the drawing to the phone GPU, which will let you keep CPU for calculating animations and such.
You'll find some information about using OpenGL ES for android on this website :
http://blog.jayway.com/2009/12/03/opengl-es-tutorial-for-android-part-i/
There are 6 tutorials, and the 6th one is about textures. The 2nd tutorial tells you about drawing polygon, and lines.
Last of all, you should read this book : http://my.safaribooksonline.com/book/office-and-productivity-applications/9781430226475/copyright/ii
It's about android, openGL, ...
Good luck.
Edit :
Look like I don't have enough privilege to write a comment, therefor I'll edit this :
The way I'd do it would be maybe a lot more simpler :
I'd have a surface view, with a renderer dedicated to draw as often as possible. He would draw synchronized list of items (Synchronized, because of multi thread), with position, alpha, ... no async task here, just a regular rendering thread.
The surface view would also start a thread (regular thread once again, no async task), with a List (or something like that). This thread would, every 16 ms or something, run through the animation list, apply them. (synchronously of course if it needs to change some items used by the rendering thread).
When an animation is over (like, if it's been in the list for more than 2000 ms, easy to check with function such as System.currentTimeMillis()), I'd remove it from the list.
Voila !
Then you'll tell me : hey, but if i do a lot of animation, calculation might takes longer than 16 ms, and it appears to get slower !
My answer is : there is an easy enough solution for that : in the thread that deals with animation, before applying them you do something like that :
long lastTime = currentTime;
// Save current time for next frame;
currentTime = System.currentTimeMillis();
// Get elapsed time since last animation calculus
long ellapsedTime = currentTime - lastTime;
animate(ellapsedTime);
And in your animate function, you animate more if more time elapsed !
Hope this helps.