4 Replies - 3577 Views - Last Post: 09 April 2012 - 08:08 AM

#1 Gungnir   User is offline

  • Your Imaginary Friend

Reputation: 152
  • View blog
  • Posts: 527
  • Joined: 21-May 11

Game loop problem

Posted 08 April 2012 - 05:49 PM

This is my first attempt at making anything playable in Android, and it's not going well. I'm making presumptions based upon my experience with other languages (such as C++, Python, and Java). Probably my most pressing issue at the moment, is that I can't grasp why every tutorial appears to update all game states in their respective "ClassName extends SurfaceView" classes.

In every other project remotely similar to this one (albeit on a PC) this type of class hasn't had anything implemented in it. It's supposed to be (in my mind, at least) just a class that's called from my game loop to render updates to a screen. In theory, that should work, but in practice it keeps failing.

This is probably due to my own inexperience with android devices, or perhaps a concept lost in translation.

So I have some questions of my own.

1. Is my class that extends android's Thread class supposed to hold my main loop?
2. If my class that extends SurfaceView really is meant to update states, why the hell is that, and is there any alternative that's any less confusing?
3. Can I create an entirely independent class to hold my loop?

If you need me to post code I will, but this is more about why android fundamentals are so fudging confusing.

Any links, advice, feedback, or just relative anecdotes would be greatly appreciated.

EDIT:
I just found two resources useful to my current predicament.

Android docs explain the activity life-cycle.

Rob Green details basic game fundamentals in Android

I'm still really confused but I have some new information that sounds relevant.

EDIT EDIT:

Yet another tutorial
I think I'm starting to get it. I don't like it, but I'm starting to get it.

The loop exists only to call other methods. THOSE METHODS update states. For instance, the canvas is the surface, SurfaceView just draws to the canvas (probably wrong but close enough). Before doing so, SurfaceView updates everything that needs to be drawn? NO! This is still all backwards. In the above tutorial, SurfaceView is updating the physics of the game, and then drawing to the screen, which still means that I'll have to instantiate all of my objects in SurfaceView (by which I mean, my class that extends SurfaceView) so that I can get their physics, and so that their states can change, I'll also have to create and check all of my events in SurfaceView. Before I know it, SurfaceView is yet again the body of my game and I have to start all over again because I'll wake up in the morning and realize that what I'm doing makes ABSOLUTELY NO SENSE! (inb4chinacalled)

At the end of the day, if I don't understand my engine, I don't have any control over it. I really need help guys...

Can I create a new class designed purely to hold everything in place? That is to say, I'll make a new class, with the same name as my game, I'll initialize everything and import all other classes. I'll instantiate all game, android, and event objects and define all initial variables. Then I'll make a loop as follows:

while(running)
{
	//get user input
	
	//update states based on user input and game logic
	
	//render updates to the screen, speakers, and possibly vibrate (goodbye, battery)
}
//clean up all resources and stuff



Is such a thing possible? (I should hope so)
If anybody could point me toward (or give) an example of where somebody achieves such a thing, I would be forever grateful. Because right now I can't get creative in the slightest because I don't understand what the hell I'm doing.

Alternatively, if anybody can provide a method to this madness that would be greatly appreciated. I've never seen an example of a full game, so I don't understand how it all fits together.

It's not urgent, but I would like a second opinion (or perspective) because this is really stressing me out.

This post has been edited by Gungnir: 08 April 2012 - 06:56 PM


Is This A Good Question/Topic? 1
  • +

Replies To: Game loop problem

#2 EndLessMind   User is offline

  • Android Expert
  • member icon

Reputation: 273
  • View blog
  • Posts: 1,252
  • Joined: 13-March 09

Re: Game loop problem

Posted 08 April 2012 - 08:25 PM

Okey, so I'll try to help you as much as i can.
I've never done any real games.

1: I do not believe that the class that extends android's Thread class has to be your main thread.
You can have several thread running at the same time, even on single-core-SoC based devices.

2: I have no idea about the surfaceView, but i think that you can have multiple classes that extends surfaceView.

3: basically, yes. You should maybe do that by benching it down. maybe not do everything into one class with a few methods, but maybe into 5-7 classes with a few methods each.
This way it's easier for you to customize the usage of the engine, to be used in more then one game without actually changing the engine it self.
Because you'll be able to call all the methods easy from within the classes of the actual game-classes.
Hope this makes sense :)

And a last piece of advise (about the loop in the code-block you posted)
Yes, you could to that, but i strongly recommend that you put that loop in a thread
Thread t = new Thread() {
@Override
public void run() {
 // Your loop here
}
};
t.start();


Hope this helps :)
Off-topic: And i just want to apologize for any misspells ect, its 5.22 am here right now, and i'm just going to sleep. Tho it wouldn't surprise me if i replied in the wrong topic :P
Was This Post Helpful? 1
  • +
  • -

#3 Gungnir   User is offline

  • Your Imaginary Friend

Reputation: 152
  • View blog
  • Posts: 527
  • Joined: 21-May 11

Re: Game loop problem

Posted 08 April 2012 - 09:16 PM

Your post actually helped a lot.

My loop is now contained within the Overrided thread.run() method.

This method is called by SurfaceView...

... Which in turn is called by the creation of my activity:
Spoiler


Is that correct, or should I (if it's even possible?) call my loop thread on creation of my activity, and (once again, if it's possible) setContentView(surface) within my loop? Can I create a method within androidActivity to set the Content View as a static reference during my loop, or is that a non-static method? (or just an invalid call to that particular method)

If that's not possible, that's all okay. I would just feel a lot more... 'comfortable' if I were able to start my program in a thread rather than a view.

Once again, if everything that I've just said is completely impossible or inadvisable, I suppose I could conform to the conventions contained within these tutorials, but that would require effort... .-. (effort that I would usually put into getting my Bachelor of Technology)
Was This Post Helpful? 0
  • +
  • -

#4 EndLessMind   User is offline

  • Android Expert
  • member icon

Reputation: 273
  • View blog
  • Posts: 1,252
  • Joined: 13-March 09

Re: Game loop problem

Posted 09 April 2012 - 06:24 AM

I'm not sure if that's that correct way.

The best way to go right now, would possible be to:
1. get the apk of a android game
2. DJ Java Decompiler (you can get it at TPB)
3. change the apk-file extension to .zip
4. Decompile the engine and have a look a the source code

Because, i do not know the answer to these questions.
I hope this did have you some more help.
Was This Post Helpful? 0
  • +
  • -

#5 htc(hot-taco-cheese)   User is offline

  • D.I.C Head
  • member icon

Reputation: 17
  • View blog
  • Posts: 71
  • Joined: 04-December 10

Re: Game loop problem

Posted 09 April 2012 - 08:08 AM

Hey, I might be able to help. This isn't the best example, just picking up a player and moving it around the screen. The first few tutorials here explain it better. My code isn't perfect but you should be able to understand its concepts.

In my basic games, I have three or four major classes.
  • The Activity
  • The game panel
  • The game thread
  • The Player


The Activity only has one major purpose; to hold the game panel.
public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // *optional* requesting to turn the title OFF
        requestWindowFeature(window.FEATURE_NO_TITLE);

        // *optional* making it full screen
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        // set our MainGamePanel as the View
        setContentView(new MainGamePanel(this));
    }
}



In the MainGamePanel is where things start becoming more complex. This is the class that extends SurfaceView and contains the canvas. The thread has not been made yet so this will make more sense once we make it.
public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback {

	public static final String TAG = MainGamePanel.class.getSimpleName();
	
	private MainThread thread;
	private Player player;
	
	public MainGamePanel(Context context) {
		super(context);
		getHolder().addCallback(this);
		
		//create player and load bitmap
		player = new Player(BitmapFactory.decodeResource(getResources(), R.drawable.player), 50, 50);

		//create game loop thread
		thread = new MainThread(getHolder(), this);
		
		//make the GamePanel focusable so it can handle events
		setFocusable(true);
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
        //the surface has been created and we can now use it
		thread.setRunning(true);
		thread.start();
	}

	/*
	 * This method blocks the thread and
	 * waits for it to die, shutting the
	 * thread down cleanly. We haven't made the thread yet.
	 */
	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		Log.d(TAG, "Destroying surface...");
		boolean retry = true;
		while(retry) {
			try {
				thread.join();
				retry = false;
			}
			catch(InterruptedException e) {
				//try again
			}
		}
		Log.d(TAG, "Thread shut down cleanly");
	}
	
    //handles the touch events
	@Override
	public boolean onTouchEvent(MotionEvent event) {
        //finger was pressed on the screen
		if(event.getAction() == MotionEvent.ACTION_DOWN) {
			//delegating event handling to the player
			player.handleActionDown((int)event.getX(), (int)event.getY());
			Log.d(TAG, "Coords: x=" + event.getX() + ",y=" + event.getY());
		}
		
        //finger is moving
		if(event.getAction() == MotionEvent.ACTION_MOVE) {
			if(player.isTouched()) {
				//player picked up and being dragged
				player.setX((int)event.getX());
				player.setY((int)event.getY());
			}
		}

	    //finger is released	
		if(event.getAction() == MotionEvent.ACTION_UP){
			if(player.isTouched())
				player.setTouched(false);
		}
		return true;
	}
	
	//update game state
	public void update() {
		//update the player
		player.update();
	}
	
	//render game state to screen
	protected void render(Canvas canvas) {
		canvas.drawColor(Color.BLACK);
		player.draw();
	}

}



This is a very basic game loop, but nevertheless, it works. It is pretty much just a run() method that contains a while loop that is controlled by a simple running boolean.
public class MainThread extends Thread {

	 private SurfaceHolder surfaceHolder;
	 private MainGamePanel gamePanel;
	 private boolean running;
	 
	 public void setRunning(boolean running) {
		 this.running = running;
	 }

	 public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
		 super();
		 this.surfaceHolder = surfaceHolder;
		 this.gamePanel = gamePanel;
	 }

	 @Override
	 public void run() {
		 Canvas canvas;
		 Log.d(TAG, "Starting game loop");
		 while (running) {
			 canvas = null;
			 // try locking the canvas for exclusive pixel editing on the surface
			 try {
			     canvas = this.surfaceHolder.lockCanvas();
				 synchronized (surfaceHolder) {
					 // update game state
					 // renders the canvas on the panel
					 this.gamePanel.render(canvas);
				 }
			 } 
			 finally {
				 if (canvas != null) {
					 surfaceHolder.unlockCanvasAndPost(canvas);
				 }
			 }
		 }
	 }
	 
}



The image to be drawn on the screen. It has some basic methods to get and set its state and position.
public class Player {

	 private Bitmap bitmap; // the actual bitmap
	 private int x;   // the X coordinate
	 private int y;   // the Y coordinate
	 private boolean touched; // if player is touched/picked up

	 public Player(Bitmap bitmap, int x, int y) {
		 this.bitmap = bitmap;
		 this.x = x;
		 this.y = y;
	 }

	 public Bitmap getBitmap() {
		 return bitmap;
	 }
	 public void setBitmap(Bitmap bitmap) {
		 this.bitmap = bitmap;
	 }
	 public int getX() {
		 return x;
	 }
	 public void setX(int x) {
		 this.x = x;
	 }
	 public int getY() {
		 return y;
	 }
	 public void setY(int y) {
		 this.y = y;
	 }
	 public boolean isTouched() {
		 return touched;
	 }
	 public void setTouched(boolean touched) {
		 this.touched = touched;
	 }

	//update player's state
	public void update() {
		//the player does not control itself so, we have nothing here for now
	}

	 //draw the player to the canvas
	 public void draw(Canvas canvas) {
	  canvas.drawBitmap(bitmap, x - (bitmap.getWidth() / 2), y - (bitmap.getHeight() / 2), null);
	 }

	 //if the touch is on the player, set the player's coordinates to the same as the touch
	 public void handleActionDown(int eventX, int eventY) {
		 if (eventX >= (x - bitmap.getWidth() / 2) && (eventX <= (x + bitmap.getWidth()/2))) {
			 if (eventY >= (y - bitmap.getHeight() / 2) && (y <= (y + bitmap.getHeight() / 2))) {
				 //player touched
				 setTouched(true);
			 } 
			 else {
				 setTouched(false);
			 }
		 } 
		 else {
			 setTouched(false);
		 }
	 }
	 
}


Was This Post Helpful? 0
  • +
  • -

Page 1 of 1