I am programing 2D rpg game and I have weird problem. When I use KeyPressed method it happens to call my move method twice which exits to rewrite player position from [10,10] to [10,12] insted of [10,11] and move my player 2 fields. I was looking around the internet and found nothing useful.
For displaying I use Canvas and Runnable with JFrame. A also use Thread
When I used debugger to search what's wrong I found that Keylistener use method
KeyPressed
public void keyPressed(KeyEvent e)
{
((KeyListener)a).keyPressed(e);
((KeyListener)b).keyPressed(e);
}
I believe that KeyListener a and b do the problem but I dont know hot to fix it.
My move method which change Point (x,y)
public void move() {
Point startPlayerPosition = player.getPosition();
Point newPoint = startPlayerPosition;
switch (direction) {
case Direction.NORTH:
newPoint = new Point(startPlayerPosition.x, startPlayerPosition.y - 1);
break;
case Direction.SOUTH:
newPoint = new Point(startPlayerPosition.x, startPlayerPosition.y + 1);
break;
case Direction.WEST:
newPoint = new Point(startPlayerPosition.x - 1, startPlayerPosition.y);
break;
case Direction.EAST:
newPoint = new Point(startPlayerPosition.x + 1, startPlayerPosition.y);
break;
}
playerPos = newPoint;
player.setPosition(newPoint);}
This is my KeyPressed method. I wait for pressing W,A,S,D to to change direction and then I call move() according to direction.
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_W:
playerWalk = true;
direction = Direction.NORTH;
move();
break;
case KeyEvent.VK_S:
playerWalk = true;
direction = Direction.SOUTH;
move();
break;
case KeyEvent.VK_D:
playerWalk = true;
direction = Direction.EAST;
move();
break;
case KeyEvent.VK_A:
playerWalk = true;
direction = Direction.WEST;
move();
break;
}
}
Thanks for Help
Related
Hey I'm trying to write a solver for a n x m sliding puzzle. My algorithm finds a solution but it's not the optimal one. I took a look at BFS Algorithm since it seemed pretty fitting for this kind of problem but I have no idea on how to adapt my code accordingly. I hope someone can find the problem in my code or give my some tips on how to improve it. The performance is very poor also.
private static final int maxMoves = 30;
private List<Direction> solutions = new ArrayList<Direction>();
public void solveHelper(List<Direction> moves, SlidingPuzzle g){
if(moves.size() > maxMoves){
return;
}
if(g.isGameOver()){
solutions.addAll(moves);
return;
}
for(Direction dir : Direction.values()){
Direction invDir = null;
switch(dir){
case UP:
invDir = Direction.DOWN;
break;
case RIGHT:
invDir = Direction.LEFT;
break;
case LEFT:
invDir = Direction.RIGHT;
break;
case DOWN:
invDir = Direction.UP;
break;
}
if(moves.size() == 0 || invDir != moves.get(moves.size() - 1)){
if(g.moveGap(dir)){
solveHelper(createNewListAndCopyAndAdd(moves, dir), g);
g.moveGap(invDir);
}
}
}
}
private List<Direction> createNewListAndCopyAndAdd(List<Direction> list, Direction dir){
List<Direction> newList = new ArrayList<Direction>(list);
newList.add(dir);
return newList;
}
So I have a touch event and it handles ACTION_DOWN and ACTION_UP one by one. For example if I click on the left half of the screen ACTION_DOWN works but not ACTION_UP and same for the right side of the screen. i want to handle both of them simultaneously. If I click on left side and right side at the same time both the event should perform. Can anyone please help me on this.
My code is
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//if user press the screen
if (event.getX() < screenX / 2f) {
flight.isGoingUp = true;
}
break;
case MotionEvent.ACTION_UP:
flight.isGoingUp = false;
if (event.getX() > screenX / 2f) {
flight.toShoot++;
}
break;
}
return true;
}
Edit 1
The screen is split into two half. The left side of the screen is used for flight.isGoingUp = true; and when we release the left side or tap the right helg of the screen flight.isGoingUp = false; and if (event.getX() > screenX / 2f) { flight.toShoot++; } I want ot do such a code that both of them are handled at the same time.
here is the docs to handle multi touch gestures.
https://developer.android.com/training/gestures/multi#java
Here is a very simple example to handle multitouch based on google docs.
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getActionMasked();
int index = event.getActionIndex();
System.out.println("The finger # " + index + " is " + getAction(action));
return true;
}
public static String getAction(int action) {
switch (action) {
case MotionEvent.ACTION_DOWN: return "Down";
case MotionEvent.ACTION_MOVE: return "Move";
case MotionEvent.ACTION_POINTER_DOWN: return "Pointer Down";
case MotionEvent.ACTION_UP: return "Up";
case MotionEvent.ACTION_POINTER_UP: return "Pointer Up";
case MotionEvent.ACTION_OUTSIDE: return "Outside";
case MotionEvent.ACTION_CANCEL: return "Cancel";
default: return "Unknown";
}
}
In my Snake code, if the snake is moving LEFT currently, and I then press either UP or DOWN, and then very quickly press RIGHT, the game is over. I knew the reason why it happened, but I don't know how to fix it. Maybe I should announce a new variable for last direction and then compare it with the new direction to decide if the snake should turn to the new direction? I don't know how to fix it. Can somebody help me to solve the problem ? :-)
Here is the relevant code.
//Judge the keyboard input
public void changeDirection(Direction newDirection) {
if (snakeDirection.compatibleWith(newDirection)) {
snakeDirection = newDirection;
}
Direction.java
public enum Direction {
UP(0),
RIGHT(1),
DOWN(2),
LEFT(3);
private final int directionCode;
Direction(int directionCode) {
this.directionCode = directionCode;
}
private int directionCode() {
return directionCode;
}
//
public boolean compatibleWith(Direction direction) {
return (this.directionCode() + direction.directionCode()) % 2 == 1;
}
}
GameController.java
public class GameController implements Runnable, KeyListener {
private final Grid grid;
private final GameView gameView;
boolean running;
public GameController(Grid grid, GameView gameView) {
this.grid = grid;
this.gameView = gameView;
this.running = true;
}
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
switch (keyCode) {
case KeyEvent.VK_UP:
grid.changeDirection(Direction.UP);
break;
case KeyEvent.VK_DOWN:
grid.changeDirection(Direction.DOWN);
break;
case KeyEvent.VK_LEFT:
grid.changeDirection(Direction.LEFT);
break;
case KeyEvent.VK_RIGHT:
grid.changeDirection(Direction.RIGHT);
break;
case KeyEvent.VK_ENTER:
if (running == false) {
grid.init();
running = true;
new Thread(this).start();
}
break;
case KeyEvent.VK_SPACE:
if (running) {
running = false;
} else {
running = true;
new Thread(this).start();
}
break;
default:
break;
}
}
// the Thread
public void run() {
while (running) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
break;
}
// your code here
if (grid.nextRound()) {
gameView.draw();
} else {
gameView.showGameOverMessage();
break;
}
}
running = false;
}
You really need a new variable. Your snakeDirection is the direction you want to use in the next time step and comparing it with newDirection only helps when it has been used. You need a lastUsedDirection set after a step was done.
Some comments
You can replace directionCode by ordinal().
You should start only a single thread and let it skip the action when running=false. Use the constructor allowing to start it with deamon=true, so that you can terminate the game without it terminating first. Don't let it terminate. You current code risks having multiple threads running when you press SPACE multiple times fast enough.
Be careful with you variable used in multiple threads. As long as one thread only reads the variable and the other one only writes it, it's enough to use volatile. You probably don't even do this and then it can happen that one thread never sees the writes made by another one (this is no theory; this really happens, maybe not in your case).
Just check the x and y positon of the head and the "neck".
What I mean is:
neck
|
| xxxx
| x <--- The snake
HNxx
|
|
head
If the position is the same in one axis, you should not turn.
Here's an actual example (it's C++, but Java has similar syntax, so this shouldn't be a problem).
void Snake::setDirection(char dir) {
switch (direction) {
case Snake::NORTH:
if (dir == Snake::SOUTH) return;
if (body.size() > 1 && body[0]->y == body[1]->y) return;
break;
case Snake::SOUTH:
if (dir == Snake::NORTH) return;
if (body.size() > 1 && body[0]->y == body[1]->y) return;
break;
case Snake::EAST:
if (dir == Snake::WEST) return;
if (body.size() > 1 && body[0]->x == body[1]->x) return;
break;
case Snake::WEST:
if (dir == Snake::EAST) return;
if (body.size() > 1 && body[0]->x == body[1]->x) return;
break;
}
direction = dir;
}
body is a std::vector that holds all the snake segments and direction is a Snake class variable.
I'm making a platfrom game for android and I could use some help with the touch events.
This is my code:
public boolean onTouch(MotionEvent e, int scaledX, int scaledY) {
for (int i = 0; i < object.size(); i++) {
tempObject = object.get(i);
if (tempObject.getId() == ObjectId.Player) {
if (e.getAction() == MotionEvent.ACTION_MOVE) {
if (moveLeft.contains(scaledX, scaledY)) {
tempObject.setMovingLeft(true);
tempObject.setMovingRight(false);
}
if (moveLeftExit.contains(scaledX, scaledY)
&& !moveLeft.contains(scaledX, scaledY)) {
tempObject.setMovingLeft(false);
}
if (moveRight.contains(scaledX, scaledY)) {
tempObject.setMovingRight(true);
tempObject.setMovingLeft(false);
}
if (moveRightExit.contains(scaledX, scaledY)
&& !moveRight.contains(scaledX, scaledY)) {
tempObject.setMovingRight(false);
}
}
if (e.getAction() == MotionEvent.ACTION_UP
|| e.getAction() == MotionEvent.ACTION_OUTSIDE) {
if (moveLeft.contains(scaledX, scaledY)) {
tempObject.setMovingLeft(false);
}
if (moveRight.contains(scaledX, scaledY)) {
tempObject.setMovingRight(false);
}
}
if (e.getAction() == MotionEvent.ACTION_DOWN) {
if (jump.contains(scaledX, scaledY)) {
if(tempObject.getVelY() ==0)
tempObject.setVelY(-15);
}
}
}
}
return true;
}
Everything works great while I use one finger, if I touch the moveRight rectangle the character moves right and when I move my finger away he stopes as expected. The problem is that if I touch a button while touching some other button it wont react to it.
So I guess my question is, how can I modify my code so it will react to multi touch?
Thanks!! :)
Here is a simple code.
public boolean onTouch(View v, MotionEvent event) {
int action = MotionEventCompat.getActionMasked(event);
int pointerIndex = MotionEventCompat.getActionIndex(event);
int x = (int)MotionEventCompat.getX(event,pointerIndex);
int y = (int)MotionEventCompat.getY(event,pointerIndex);
switch(action)
{
case MotionEvent.ACTION_DOWN:
//
// First finger was touched. Set "pointerIndex" to some value (lets say 0 or -1)
// Save "pointerIndex" corresponding to this touched object.
//
break;
case MotionEvent.ACTION_POINTER_DOWN:
//
// More finger touched when first finger is already touched.
// Save "pointerIndex" corresponding to this touched object.
//
break;
case MotionEvent.ACTION_MOVE:
//
// Finger with "pointerIndex" was moved.
//
break;
case MotionEvent.ACTION_UP:
//
// The first touched finger went off.
//
break;
case MotionEvent.ACTION_POINTER_UP:
//
// Finger with "pointerIndex" went off (other than first finger)
//
break;
}
return true;
}
Good Luck. :)
I am developing a two player Pong game and wish to control the bats, each with one finger. However, my problem is that it only responds to one finger being on the screen at a time. The entire game is ran in one view. First, I check if a pointer is "active" before checking if it occured on the top or bottom of the screen before moving the corresponding bat. It works normally, except for that only one touch registers. My code for the method is below:
// variables
int _touchY = (int) event.getY();
boolean isTopSide = true;
// test if motionEvent was on the top or bottom of the screen
if (SCREEN_HEIGHT / 2 - _touchY < 0) {
isTopSide = false;
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
_activePointer = event.getPointerId(0);
break;
case MotionEvent.ACTION_MOVE:
if(_activePointer != INVALID_POINTER_ID) {
if (isTopSide) { // top bat
_topBat.moveBat(event);
} else { // bottom bat
_botBat.moveBat(event);
}
}
if(_newPointer != INVALID_POINTER_ID){
if (isTopSide) { // top bat
_topBat.moveBat(event);
} else { // bottom bat
_botBat.moveBat(event);
}
}
break;
case MotionEvent.ACTION_UP:
if(_activePointer != INVALID_POINTER_ID){
_activePointer = INVALID_POINTER_ID;
}
break;
case MotionEvent.ACTION_CANCEL:
_activePointer = INVALID_POINTER_ID;
break;
case MotionEvent.ACTION_POINTER_UP:
int pointerIndex = event.getActionIndex();
int pointerId = event.getPointerId(pointerIndex);
if(pointerId == _activePointer){
_activePointer = INVALID_POINTER_ID;
}
case MotionEvent.ACTION_POINTER_DOWN:
int newPointerIndex = event.getActionIndex();
_newPointer = event.getPointerId(newPointerIndex);
break;
}
Relevant global code
// touched
private static final int INVALID_POINTER_ID = -1;
private int _activePointer = INVALID_POINTER_ID;
private int _newPointer = INVALID_POINTER_ID;
The main problem why you're only seeing values for a single pointer is because event.getY() without any parameter gives you the position for the first pointer index, even if the pointer that actually moved was the second pointer.
The MotionEvent class contains the coordinates for all the active pointers.
If you want to get the second pointer's position, you'll want to call event.getY(1).
You'll probably want to loop through all the active points, getting the total count via event.getPointerCount(), and then send the events accordingly.