I am making a java 2d side scroller and im having problems with multiple keys at the same time. Such as right + up. Whenever you release the up you stop moving right as you slowly go back to the ground even though right is still being pressed. Here are the keyboard listeners I have setup. dx is my horizontal movement speed and dy is the vertical height. Below is from the character class
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) {
dx = -5;
mainCharacterImageFacing = l.getImage();
}
if (key == KeyEvent.VK_RIGHT) {
dx = 5;
mainCharacterImageFacing = r.getImage();
}
if (key == KeyEvent.VK_UP) {
dy = 1;
mainCharacterImageFacing = r.getImage();
}
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT);
dx = 0;
if (key == KeyEvent.VK_RIGHT)
dx = 0;
if (key == KeyEvent.VK_UP)
{dy = 0;
mainCharacterImageFacing = r.getImage();
}
This is some code from the main game window that deals with calling the key press/release methods as well as dealing with the jump.
private class AL extends KeyAdapter{
public void keyReleased(KeyEvent e) {
p.keyReleased(e);
}
public void keyPressed(KeyEvent e) {
p.keyPressed(e);
}
}
#Override
public void run()
{
long beforeTime;
long timeDiff;
long sleep;
beforeTime = System.currentTimeMillis();
while(done == false)
{
cycle();
timeDiff = System.currentTimeMillis() - beforeTime;
sleep = 10 - timeDiff;
if (sleep < 0 )
sleep = 2;
try {
Thread.sleep(sleep);
} catch (Exception e)
{}
beforeTime = System.currentTimeMillis();
}
done = false;
h = false;
k = false;
}
boolean h = false;
boolean done = false;
public void cycle() {
if (h == false)
v = v - 2; //jump speed falling
if (v == 350) //max y value of jump. Lower numbers = higher jumps
h = true;
if (h == true && v <= 470) //starting y value
{
v = v + 2; //jump speed rising
if (v == 470)
done = true;
}
}
Do not handle your movement directly in the AL class. Instead, use booleans and set them to true/false accordingly:
private class AL extends KeyAdapter{
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) {
left = true;
}
if (key == KeyEvent.VK_RIGHT) {
right = true
}
if (key == KeyEvent.VK_UP) {
//...
}
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT)
left = false
if (key == KeyEvent.VK_RIGHT)
right = false
if (key == KeyEvent.VK_UP)
//...
}
}
The reason for this is because keyPressed and keyReleased are called when the keys are acted upon, though that means it will not be in sync with your main loop.
To move your sprite call a method in the loop which, if your conditions are true, moves your sprite accordingly, else does nothing.
EDIT: After reviewing your code some more I have a few suggestions.
First of all, look up sub-pixel rendering. Sub-pixel rendering is used to make the movement of sprites look smoother.
Another thing to look up is variable timesteps (sometimes referred to as delta timesteps). Delta time can be used to make sure that actions are performed in sync with a players fps. If one player has 60fps and another has 30fps, the player with 30fps will see actions performed at half the speed. With a delta timestep however, actions are always performed at the same speed. Here's a good article.
The third thing is your timing. Currently you're using System.getCurrentTimeMills. This is bad, and not precise. Instead, use System.nanoTime.
A fourth thing I've spotted is your jumping algorithm. A better way is to include acceleration. First you need to declare a constant for acceleration (0.1 or 0.2 are good) and tally a vertical speed. You can then fall when the vertical speed gets to a certain value. Here's a small snippet, though if you have gravity you may want to edit it. First of all set vSpeed to say -8 by default.
public void jump() {
vSpeed += ACCELERATION;
if(vSpeed < 8)
dy += vSpeed;
else
vSpeed = -8;
falling = false;
}
I just wrote this off the top of my head, but basically, since moving upwards is taking away a number, you appear to jump when it adds negative numbers, then you'll start falling when it reaches 0, and land when reaches 8 again.
The problem is the way the OS handles key events and passes them to your application. When you press a key, it fires an event for down. Then based on your settings for repeating, it will start pressing the key again after some amount of time, at some amount of frequency. (you can see this when typing. Press a key, and hold it).
When you press another key, it stops the repeated triggers for the other key. You can see this while typing as well. Hold a key, then start holding another. The original key stops, as you would expect.
This won't even be consistent with every os, and people could cheat in your game as it is, by making the repeat timer on their keyboard faster.
For these reasons, it's a very bad idea to do animations inside of key listeners. You should be settings flags to true/false and reading them in a game loop. (As demonstrated in #Troubleshoot's answer).
Related
So the object that I've drawn starts moving either up or down depending on what arrow key I've pressed but once i let go it carries on moving in the same direction not stopping unless i press a random key. If i press up it starts moving up and carries on moving up even after letting go of up. If i then press down it starts moving down and carries on moving down even after i am no longer pressing it. If any key other than up arrow key or down arrow key is pressed whilst it is moving it stops moving.
MAIN SKETCH:
Defender defender1;
PImage background;
int x=0; //global variable background location
void setup() {
size(800,400);
background = loadImage("spaceBackground.jpg");
background.resize(width,height);
defender1 = new Defender(200,200);
}
void draw () {
image(background, x, 0); //draw background twice adjacent
image(background, x+background.width, 0);
x -=4;
if(x == -background.width)
x=0; //wrap background
defender1.render();
defender1.keyPressed();
}
Defender Class:
class Defender {
int x;
int y;
Defender(int x, int y) {
this.x = x;
this.y = y;
}
void render() {
fill(255,0,0);
rect(x,y,50,20);
triangle(x+50,y,x+50,y+20,x+60,y+10);
fill(0,0,100);
rect(x,y-10,20,10);
}
void keyPressed() {
if (key == CODED) {
if (keyCode == UP) {
defender1.y = y-1;
} else if (keyCode == DOWN) {
defender1.y = y+1;
}
}
}
}
You're calling your defender1.keyPressed() function every time draw() is called.
The key and keyCode variables hold the most recently pressed key, regardless of whether that key is currently being held down.
To check whether any key is currently being pressed, you could use the keyPressed variable.
Or you could change your code to only call defender1.keyPressed() from the sketch-level keyPressed() function.
You can find more info in the reference.
Shameless self-promotion: here is a tutorial on user input in Processing.
I understand the basis of collision detection and have developed my way (as far as I know) for collision detection. However, it does not seem to be working. What I have done is instead of drawing full rectangles around sprites, shapes, and other objects, I simply made methods that draw lines (very thin rectangles) on all sides of the object, left, right, top, and bottom.
The methods that draw the thin rectangles on the left,right,top, and bottom of the specified sprites. This is probably not the best way to do collision, but this is what I have, and am open to all ideas and different solutions!
Note: The(int)(x-Game.getCamera().getX()),(int)(y + height -Game.getCamera().getY()), is simply for the camera, so when the player moves, the trees (in my case the object I want to collide with) do not move with him
public Rectangle getBounds() {
return new Rectangle((int)(x-Game.getCamera().getX()),
(int)(y + height - Game.getCamera().getY()),width,height-height);
}
public Rectangle getBoundsTop() {
return new Rectangle((int)(x-Game.getCamera().getX()),
(int)(y- Game.getCamera().getY()),width,height-height);
}
public Rectangle getBoundsRight() {
return new Rectangle((int)(x + width -Game.getCamera().getX()),
(int)(y - Game.getCamera().getY()),width - width,height);
}
public Rectangle getBoundsLeft() {
return new Rectangle((int)(x -Game.getCamera().getX()),
(int)(y- Game.getCamera().getY()),width - width,height);
}
Actual collision CHECKING (in player class)-
if(getBounds().intersects(treeTile.getBoundsTop())) {
//What happens when they collide
//When the bottom of the player collides with the top of the tree.
}
if(getBoundsTop().intersects(treeTile.getBounds())) {
//What happens when they collide
//When the top of the player collides with the bottom of the tree
}
if(getBoundsRight().intersects(treeTile.getBoundsLeft())) {
//What happens when they collide
//when the right side of the player collides with the left of the
//tree
}
if(getBoundsLeft().intersects(treeTile.getBoundsRight())) {
//What happens when they collide
//When the left of the player collides with the right of the tree
}
I appreciate all the help I can get
Here is collision and movement code-
for (int i = 0; i < handler.object.size(); i++) {
Square handle = handler.object.get(i);
if (getBounds().intersects(treeTile.getBoundsTop())) {
handle.setvelY(-1);
}
if (getBoundsTop().intersects(treeTile.getBounds())) {
handle.setvelY(0);
}
if (getBoundsRight().intersects(treeTile.getBoundsLeft())) {
handle.setvelX(0);
}
if (getBoundsLeft().intersects(treeTile.getBoundsRight())) {
handle.setvelX(0);
}
}
Movement-
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
for (int i = 0; i < handler.object.size(); i++) {
Square handle = handler.object.get(i);
if (handle.getId() == ObjectId.Zelda) {
if (code == KeyEvent.VK_D) {
handle.right();
}
if (code == KeyEvent.VK_A) {
handle.left();
}
if (code == KeyEvent.VK_W) {
handle.up();
}
if (code == KeyEvent.VK_S) {
handle.down();
}
}
My new collision idea -
boolean walkRight = true;
boolean walkLeft = true;
boolean walkDown = true;
boolean walkUp = true;
public void tick() {
if(walkRight == true) {
velX = 2;
}if(walkLeft == true) {
velX = -2;
}if(walkDown == true) {
velY = 2;
}if(walkUp == true) {
velY = -2;
}
if (getBounds().intersects(treeTile.getBoundsTop())) {
walkDown = false;
}
if (getBoundsTop().intersects(treeTile.getBounds())) {
walkUp = false;
}
if (getBoundsRight().intersects(treeTile.getBoundsLeft())) {
walkRight = false;
}
if (getBoundsLeft().intersects(treeTile.getBoundsRight())) {
walkLeft = false;
}
Something like this, not exactly, it is just an example.
Have you tried using 1 instead of width - width and height - height ?
I think these evaluating to 0 for the width / height is causing the intersects function to fail.
EDIT
I think you're over complicating this. If you just have one big collision box, instead of four small ones, then where ever you're moving your player, you can
// Square previousPosition = currentPosition
// do move player code, which presumably updates the currentPosition
// loop through all the trees you have
// if the player is colliding with a tree
// currentPosition = previousPosition
This should handle the player colliding with any side of the tree, since we just move them back to where they were previously.
I have been working on android apps for a long time but now I have decided to create a game aside with my pre-calculus final. I have completed the whole code and it works perfectly except one tiny issue. First of the game is based on flying pig(my classmate's face) avoiding top and bottom osticle. I developed an algorithm so that the obsticles are evenly spaced and based on random selection placed either as the top or bottom of the screen but never both at the same time!. The algorithm that needs improvement is in the 3rd code segment!
This is the screenshot of the problem: screenshot here
(My account is new so stackoverflow wont let me to share photos directly)
This is the class that calls updates for all dynamic objects (ship = pig, bacon = bonus item, BM = BarrierManager class's update() which updates the obsticles)
//this will update the resources
void Update(float dt) {
ship.update(dt);
//bumbing
if (!ship.death) {
background.update(dt);
**BM.update(dt);**
for (int i = 0; i % 5 == 0; i++) {
bacon.update(dt, BM.position);
}
}
ArrayList<Point> bacon_point = new ArrayList<Point>(bacon.getArray());
if (ship.bump(bacon_point.get(0), bacon_point.get(1), bacon_point.get(2), bacon_point.get(3))) {
bacon.setX(-200);
bacon.setY(-200);
Message msg = BM.game_panel.game.handler.obtainMessage();
msg.what = 0;
BM.game_panel.game.handler.sendMessage(msg);
}
for (int i = 0; i < BM.TopWalls.size(); i++) {
ArrayList<Point> temp = new ArrayList<Point>(BM.TopWalls.get(i).getArray());
//after we have accest the TopWalls arraylist we can call bump that check TopWalls object's points position with the pig's points
ArrayList<Point> temp2 = new ArrayList<Point>(BM.BottomWalls.get(i).getArray());
//after we have accest the TopWalls arraylist we can call bump that check BottomWalls object's points position with the pig's points
if ((ship.bump(temp.get(0), temp.get(1), temp.get(2), temp.get(3))) || (ship.bump(temp2.get(0), temp2.get(1), temp2.get(2), temp2.get(3))) || ship.death) {
ship.death = true;
game.onStop();
while(f==0) {
MediaPlayer mp = MediaPlayer.create(game, R.raw.explosion_fart);
mp.start();
f++;
}
f++;
Message msg = BM.game_panel.game.handler.obtainMessage();
msg.what = 1;
BM.game_panel.game.handler.sendMessage(msg);
i = BM.TopWalls.size()-1;
if(f == 8){
thread.setRunning(false);
}
}
}
}
In the BarrierManager I have created this update method which takes float dt = MainTheards general time for the game.
TopWalls is ArrayList this ArrayList is composed of top obsticles. Bottom walls is the same but BottomWalls.
//zreb decides if it should create TopWalls or BottomWalls object. This parameter is later transfered to the Barier.update method where I work with it
public void update(float dt){
for (int i=0;i<Num+1; i++) {
int zreb = new Random().nextInt(2);
//{if} to make the spacing bigger
if (i % 5 == 0){
**TopWalls.get(i).update(dt, true, zreb);
BottomWalls.get(i).update(dt, false, zreb);**
if(zreb == 0){
position.set(TopWalls.get(i).getX(), TopWalls.get(i).getY());
}
else{
position.set(BottomWalls.get(i).getX(),BottomWalls.get(i).getY());
}
}
}
}
Now this algoritm in the Barrier.class is where the magic went wrong. This update method takes the float dt mentioned earlier, a boolean variable for determining if the obsticle we work with at that instance is the Top or Bottom, and the zreb random int that determines if the top or bottom obsticle is going to be shown.
//problem! needs to be discussed
public void update(float dt, boolean b, int zreb) {
//checking if the barrier is still there
if (x<-bitmap.getWidth()){
//'b'is passed from the Barriermanager - 'update' method, determining if we have to use monkey-true or farmer-false
int raz = 200;
int vyska = BM.dpos;
//'zreb' helps me to randomly determine if monkey or ballard is going to appear
//here it determines if we are going to have Top or Bottom obsticle in the game
if(zreb == 1) {
vyska = BM.dpos - raz;
}
else {
vyska = BM.dpos + raz;
}
//koniec tohto trienedia
if (b)
{
//setting the y value for the top wall
y = vyska - BM.dl/2 - bitmap.getHeight()/2;
}
else{
//setting the y value for bottom wall
y = vyska + BM.dl/2 + bitmap.getHeight()/2;
}
//setting x-value
x = (int) (x +bitmap.getWidth()*(BM.TopWalls.size()-1));
}
x = (int) (x - BM.game_panel.ShipSpeed*dt);
}
Do you have any idea why this "one-or-the-other" condition is not working properly?
This would help me lot because this error made me deactivate the app from the store.
I am working with disabled children, who have cerebral palsy. One child has limited fine motor control, so she currently uses a joystick to control the mouse, and it's traverse rate is set very low. This works well for her, as she can click all buttons on the screen, but I think we could do better - when she wants to traverse the whole screen it takes an age to do so (about 10 seconds).
My hypothesis is that her brain is fine, and only her motor control is poor. If that's true, I believe that a mouse that started at a low speed, but experienced a constant acceleration, would be better for her, as it would pick up speed and become quick when traversing the whole screen.
If this works, then we could be tweaking PID control, and velocity/acceleration setting for a great number of disabled people, speeding up their access and hence their learning and development.
But I do not know the best way to build this - all suggestions, thoughts, links and tips welcome.
To begin with I have tried using Processing and Java, and using a mouseListener, and a Robot to control the cursor. I'm not convinced that this is the best way though, as I'm reading the cursor position and then writing to it, so my best attempts still make the cursor jump around, and there is not smooth movement. Is it achievable in Java? Do I need to read the mouse input from the USB using some kind of driver, and then replace the plotting of the cursor on screen with my own?
I've made a couple of videos to illustrate the effect I am trying to bring about.
The Status quo (my illustration is driving the cursor off the arrow keys)
http://www.youtube.com/watch?v=3ZhQCg5DJt8
The new behaviour I want (mouse accelerates)
http://www.youtube.com/watch?v=JcBK_ZCFGPs
if it's any use, the Processing code I used to make those demos is copied in below:
Status quo demo:
import java.awt.AWTException;
import jav
a.awt.Robot;
Robot robot;
int i = 0;
int j = 0;
void setup() {
setupDotgame();
try {
robot = new Robot();
}
catch (AWTException e) {
e.printStackTrace();
}
robot.mouseMove(screenWidth/2, screenHeight/2);
}
void draw() {
//println(frameCount);
robot.mouseMove(screenWidth/2+8*i, screenHeight/2+8*j);
drawDotgame();
}
void keyPressed() {
if (keyCode == UP) {
j--;
}
else if (keyCode == DOWN) {
j++;
}
else if (keyCode == RIGHT) {
i++;
}
else if (keyCode == LEFT) {
i--;
}
}
Desired behaviour:
import java.awt.AWTException;
import java.awt.Robot;
Robot robot;
int i = 0;
int j = 0;
int delta = 8;
int time = 0;
void setup() {
setupDotgame();
try {
robot = new Robot();
}
catch (AWTException e) {
e.printStackTrace();
}
robot.mouseMove(screenWidth/2, screenHeight/2);
}
void draw() {
//println(frameCount);
robot.mouseMove(screenWidth/2+i, screenHeight/2+j);
drawDotgame();
}
void keyPressed() {
if (millis() - time < 90) {
delta += 8;
}
else { delta = 8; }
time = millis();
if (keyCode == UP) {
j-=delta;
}
else if (keyCode == DOWN) {
j+=delta;
}
else if (keyCode == RIGHT) {
i+=delta;
}
else if (keyCode == LEFT) {
i-=delta;
}
}
And the DotGame code they both reference:
void setupDotgame() {
size(1000, 600);
background(255);
fill(255, 0, 0);
noStroke();
smooth();
drawCircle();
}
void drawDotgame() {
if (get(mouseX, mouseY) != color(255)) {
background(255);
drawCircle();
}
}
void drawCircle() {
int x = round(random(50, width-50));
int y = round(random(50, height-50));
int rad = round(random(20, 80));
ellipse(x, y, rad, rad);
}
Thanks in advance
as Carl suggested, I believe the best answer short of making the mouse actually have this behavior, is some sort of jumping behavior that will get the mouse fairly close to where you need to go, then use the joystick from there.
I happen to know that a program called AutoItv3 can do this sort of thing. You could set it up to recognize special hotkeys, then have that hotkey move the mouse to whatever region you wish.
Commands useful for this are HotKeySet, MouseMove, and Func/EndFunc.
I have already got the jumping on the y-axis running fine, but I want to be able to use 'dx' from jumping while pressing the left/right key (ex. if I am pressing right key to go right and jump while still holding it, the character will move in a "diagonal" way)
My main jumping code (timer activated when jump key is pressed):
public void actionPerformed (ActionEvent e) {
dy += 1;
y_pos += dy;
if (y_pos >= 400) {
dy = 0;
y_pos = 400;
timer.stop();
}
repaint();
}
Now my KeyEvent code:
if (command == KeyEvent.VK_RIGHT){
x_pos += 5;
right = true;
}
if (command == KeyEvent.VK_LEFT) {
x_pos -= 5;
right = false;
}
if (command == KeyEvent.VK_UP) {
if (!(timer.isRunning()))
dy = -20;
timer.start();
}
dr.repaint();
Below is how I would implement the input logic. Note that there are several choices when you want to dig further into input handling. Using this implementation, keyboard inputs are separated from the effects they have on the game world, which is handy in several ways. For example, the speed of the character is constant (given the timing of the game loop / movements is handled correctly) regardless of the frequency of key presses / repeats.
Anyway, some pseudo code:
// Global:
array keysdown;
keysdown[left] = false;
keysdown[right] = false;
keysdown[up] = false;
// The all-important game loop:
while (true) {
pollInputs();
doMovement();
draw();
}
function pollInputs () {
foreach (key that you want to handle) {
if (key.state == down) {
keysdown[key] = true;
} else {
keysdown[key] = false;
}
}
}
function doMovement () {
if (keysdown[left]) {
// move character left
} else if (keysdown[right]) {
// move character right
}
if (keysdown[up]) {
// initiate jump
}
}