8 way movement in MIDP 2.0

Started by
15 comments, last by spillz 18 years, 4 months ago
I'm trying to expand on this snippet of code that I got off of a tutorial. What I'm trying to do is to expand it so that I can move diagonally. I already know that the diagonals have to be mapped on the number pad, I'm just not sure as to how to move a sprite on both axes simultaneously. Here's what I want to expand on: static final int MOVE = 4//How many pixels to move in every iteration. public void moveLeft() { getXY();//This method calls up the x and y coordinates of the GameCanvas a.k.a. the game screen. if (x - MOVE > 0)//To move left, you would have to make sure that the difference of the x value and the move variable are greater than zero so that the sprite is contained in the screen. move(MOVE * -1,0);//The move method takes in two parameters: x and y, but in a certain way, like to move a sprite over the X plane, a positive x would move it to the right while an negative x moves it to the left. The same goes for the Y plane in that a negative y moves it up while a positive moves it down. } public void moveRight() { getXY(); if (x + MOVE + frameWidth < scnWidth)//This condition contains the sprite movements within the width of the screen in order to move right, so that the sprite does not move out of sight. move(MOVE,0); } public void moveUp() { getXY(); if (y - MOVE > 0)//This is just like moveLeft() in that the y value is negative. move(0,MOVE * -1); } public void moveDown() { getXY(); if (y + MOVE + frameHeight < scnHeight) move(0,MOVE);//JUst like moveRight(), it moves the sprite down, but only up to the very bottom of the screen. }
Advertisement
quick and dirty would be to call (for up and left)

public void moveUpLeft(){
moveLeft();
moveUp();
}

problem is the sprite will move much quicker going along diagonals. the distance of the diagonal move can be worked out with pythagoras theorem:

MOVE^2=XMOVE^2 + YMOVE^2

obviously MOVE is always more than XMOVE (horizontal) or YMOVE (vertical)

so assuming you want a diagonal move to be the same distance as move and assuming you want your diagonal move to be comprised of a fixed horizontal and vertical move (DMOVE) then you set

DMOVE = MOVE/(2^0.5)

where 2^0.5 ~ 1.41 = 141/100 (~ means approximately equal to)

so now you could write your diagonal move code to be something along the lines of:

static final int DMOVE = MOVE*100/141 // if MOVE =4 then DMOVE will round to 3 so your diag move will take you 3 pixels horizontally and 3 pixels vertically

public void moveUpLeft(){
getXY();
if(x-DMOVE>0&&y-DMOVE>0) // check that the sprite stays on screen
move(-DMOVE,-DMOVE);
}

the other directions will be similar.

some other things to think about:

1. do you want the sprite to be able to go all the way to the edge of the screen? your current code won't allow this unless the screen dimensions happen to be multiples of 4.

2. you are using lots of method calls for very simple code. for instance you call moveLeft which then calls getXY and move. while academics love embedding even the smallest new functionality in a new method, on devices with limited performance (like mobiles) this can slow down your program. all of this movement could be combined into a sequence of if statements without any method calls, just directly manipulating the x and y variables. its probably not a big deal for a simple program but as your program gets more complex all these redundant method calls will mean you have less time to paint sprites on the screen.

Quote:Original post by spillz
1. do you want the sprite to be able to go all the way to the edge of the screen? your current code won't allow this unless the screen dimensions happen to be multiples of 4.

2. you are using lots of method calls for very simple code. for instance you call moveLeft which then calls getXY and move. while academics love embedding even the smallest new functionality in a new method, on devices with limited performance (like mobiles) this can slow down your program. all of this movement could be combined into a sequence of if statements without any method calls, just directly manipulating the x and y variables. its probably not a big deal for a simple program but as your program gets more complex all these redundant method calls will mean you have less time to paint sprites on the screen.


1) Yeah, as long as I'm able to contain the sprite within the dimensions of the screen while the BG scrolls. As i said, since the tutorial I got the code from is tailored for a vertical side scroll shooter, I plan on making a horizonal side scroller. FYI, the dimensions of the phone I'm making it for (Siemens M56) are 101x80, so in theory I could change it to move in fours.

2) I guess the author of the tutorial managed to modularize the sprite movements, hence the snippet is actually part of a class that draws out a player sprite and he calls its methods in a game screen class encapsulated in a tick method.

Anyways, I'll give the code a try tomorrow, since I gotta get some sleep now, got a early morning class to get to.
OK, since that new method won't work until I get a scrolling tiled layer, I was wondering if there was another way to do this. Consider this following method:

private void input() {
int keyStates = getKeyStates();

sprite.setFrame(0);

// Left
if ((keyStates & LEFT_PRESSED) != 0) {
currentX = Math.max(0, currentX - 1);
sprite.setFrame(1);
}

// Right
if ((keyStates & RIGHT_PRESSED) !=0 )
if ( currentX + 5 < width) {
currentX = Math.min(width, currentX + 1);
sprite.setFrame(3);
}

// Up
if ((keyStates & UP_PRESSED) != 0) {
currentY = Math.max(0, currentY - 1);
sprite.setFrame(2);
}

// Down
if ((keyStates & DOWN_PRESSED) !=0)
if ( currentY + 10 < height) {
currentY = Math.min(height, currentY + 1);
sprite.setFrame(4);
}
}
.
.
.
.
private void drawScreen(Graphics g) {
//typical drawing goes here.
sprite.setPosition(currentX,currentY);
//same here too.
flushGraphics();
}

What the input method does is that it constrains movement to each side. In other words, for example, the left algorithm sets the current X position from the 0, or the left hand side of the background, to the current X position being subtracted by 1. Bottom line, each direction is referenced from a max point in each side, and so the X and Y values are either added or subtracted towards or away from that point. How would I apply the same pythagorean concepts to this method so that it pipes diagonal points to the X and Y variables so that they can be repainted?
your latest version is a big improvement...

a few notes:

firstly, by default that code will handle diagonal movement for any device that handles multiple key presses since you check each key independently (e.g. up and left simulteneously will move up and left by 1 space in each direction).

as before the diagonal movement distance will be greater than a vertical/horizontal movement, but this isn't necessarily a problem (depends on what you want to acheive in your game). if you wanted to implement a fixed distance move, you will need to increase the basic movement step to something like 3 units for a horizontal or vertical movement, and use say 2 units horizontal and vertical for a diagonal movement since you can't move fractions of a pixel (ALTERNATIVELY you could scale up your X,Y coordinates by 10 or 100 giving you a finer grid of movement to work with - just integer divide the coordinates by 10 or 100 before you draw the sprites to the screen).

example code for simple fixed distance key processing:

//first handle possible combination of diagonal key press pairs
if((keyState&UP)!=0&&(keyState&LEFT)!=0){
currentX = Math.max(0, currentX - 2);
currentY = Math.max(0, currentY - 2);
}
else
if(/*other diagonal keyStates*/){
...
}
...
else //now handle the vertical/horizontals
if(((keyState&UP)!=0){
currentY = Math.max(0, currentY - 3);
}
else
...

if you want an actual key for each of the diagonal moves you cannot really use the getKeyStates methods, because it only has "game" keys that don't necessarily map to specific number buttons (they're device dependant). to map direction to actual number keys override the onKeyPressed and onKeyReleased methods to set your own keyState variable(s). search threads here or on www.j2me.org for plenty of tutorials on this (i can give you sample code if necessary)

you should also think about making your code reasonably screen size independent. there is plenty of variation in screen resolution and aspect ratios. you don't necessarily need to use the maximum resolution available, but its nice to center your game on screen and position status indicators sensibly.
Well, I can safely say that good 'ol Pythagorean Theroem paid off when it came to moving the sprite diagonally, as well as optimizing the process since I encapsulated it in a method in a seperate Sprite class called up in the game screen class, which uses GameCanvas, so that's already taken care of. However, I face a new problem. You're right, I have to map out the diagonals to key codes since key states only look out for key presses in the upper half of the phone (d-pad, soft buttons, etc...). Problem is, how do I invoke them in the main game loop since a key state can be encapsulated in a method and piped through the loop? What makes this even worst is the so called bug that my phone maker's game API (Siemens Game API for MIDP 1.0, which is their own implementation of the MIDP 2.0 Game API) has, in which if I override the keyPressed, keyReleased, or keyRepeated methods, it will always set my key states to zero, thus I can't move the sprite at all. Also, it seems that my phone's emulator automatically maps key states to 2, 4, 6, and 8, so my only concern is how do I map the diagonal methods into 1, 3, 7, and 9, as well as invoking them in the main game loop: Enclosed is the example code that I've been working on as well as the link to a thread in the Siemens (now Benq Mobile) developer's forum about the bug's discovery:

http://agathonisi.erlm.siemens.de:8080/jive3/thread.jspa?forumID=6&threadID=15784&messageID=57992#57992

the code:

EDIT: new code is a few posts down.

EDIT2: Also enclosed is a thread over in J2ME.org in which another user reports of the same flaw.

http://www.j2me.org/yabbse/index.php?board=12;action=display;threadid=5068

[Edited by - thelegendofzaku on December 1, 2005 10:08:29 PM]
try calling super(keyCode) in your onKeyPressed and onKeyReleased method overrides. otherwise implement all key strokes (including game keys) yourself. be careful because the keyCodes have different codes than getKeyStates
Quote:Original post by spillz
try calling super(keyCode) in your onKeyPressed and onKeyReleased method overrides. otherwise implement all key strokes (including game keys) yourself. be careful because the keyCodes have different codes than getKeyStates


I tried mapping key strokes but I can't seem to invoke them in the main game loop. What's the best way of mapping the keys assuming I encapsulated them in a keyPressed method, since I tried looking on the web for those tutorials and could not find one that dealt with using those methods above and passing it through a main game loop, almost all of them deal with key states and others deal with the above methods in a 1.0 Canvas, not the Game API's GameCanvas class. Feel free to change part of the code to reflect such.
keyPressed and keyReleased are invoked from the midlets main thread not your game thread (most of the applications messages are passed to the midlets main thread by default). you shouldn't do any extensive processing on the main thread, just record the key events. one way to do this is to declare boolean variables in the gamecanvas for the state of each of the keys: true if pressed, false if released. in your method overrides just update the status of the keys. save the actual movement for the input method based on what key is being pressed.

also, do try calling super(keyCode) from your onKeyPressed/onKeyReleased override as this may resolve the getKeyStates issue (calling super calls the Siemens API implementations of these if there are any). i would be interested to see whether the Siemens API is implementing the gamecanvas' getKeyStates methods by overriding onKeyPressed/onKeyReleased.
I managed to move the sprite w/o using key states. All I did was use the keyPressed method in the game canvas and piped the move method in the main game loop using a threaded loop. However, I'm having trouble mapping it out to 1, 3, 7, and 9 using this method. The way I was gonna approach this is I was gonna use the getGameActions method for the d-pad and the getKeyCode method for the rest of the keys. Problem is, that oviously, the getGameActions method only gets the codes for the upper d-pad and soft keys. How would I use the getKeyCode method to effectively map the key numbers? Here's what I got so far:

import com.siemens.mp.color_game.*;
import javax.microedition.lcdui.*;

public class ExampleGameCanvas extends GameCanvas implements Runnable {
//Other variables
private int gameAction = 0;
private int keyCodes = 0;

//necessary objects declared here.

// Sprites to be used
private GreenThing playerSprite;
private Sprite backgroundSprite;

// Layer Manager
private LayerManager layerManager;

// Constructor and initialization
public ExampleGameCanvas() throws Exception {
super(true);
//Sprite creation and BG code goes here.

}

// Automatically start thread for game loop
public void start() {
isPlay = true;
Thread t = new Thread(this);
t.start();
}

public void stop() { isPlay = false; }

// Main Game Loop
public void run() {
Graphics g = getGraphics();
while (isPlay == true) {

switch(gameAction){
case UP:
playerSprite.moveUp();
break;
case DOWN:
playerSprite.moveDown();
break;
case LEFT:
playerSprite.moveLeft();
break;
case RIGHT:
playerSprite.moveRight();
break;
case 0:
switch(keyCodes){
case KEY_NUM1:
playerSprite.moveUpLeft();
break;
case KEY_NUM3:
playerSprite.moveUpRight();
break;
case KEY_NUM7:
playerSprite.moveDownLeft();
break;
case KEY_NUM9:
playerSprite.moveDownRight();
break;
}
break;
}
drawScreen(g);
try { Thread.sleep(delay); }
catch (InterruptedException ie) {}

}
}

// Method to Handle User Inputs

.
.
.
public void keyPressed(int keyCode){
super.keyPressed(keyCode);
gameAction = 0;
gameAction = getGameAction(keyCode);
//keyCodes = getKeyCode(keyCode);
}
public void keyReleased(int keyCode){
super.keyReleased(keyCode);
gameAction = 0;
}


// Method to Display Graphics


}

[Edited by - thelegendofzaku on December 1, 2005 11:03:52 PM]

This topic is closed to new replies.

Advertisement