I am creating a 2D tank game and I want my tank to be able to shoot immediately once the fire button is pressed and then again every half second, while the fire button is held. Currently In my game, my first bullet shoots immediately after the fire button is pressed, then there is a delay (I presume half second) until my tank starts shooting a stream of bullets. I'm wondering why the initial delay after the first bullet works, but the successive ones fail. Below I have included the method which creates bullets which is called every frame in my 144Hz main game loop:
public void addBullets(ArrayList<Animate> animates, ArrayList<Drawable> drawables){
if (this.ShootPressed) {
if( firstShot || (System.currentTimeMillis() - timeSinceLastShot) >= 500) {
Bullet newBullet = this.addBullet();
animates.add(newBullet);
drawables.add(newBullet);
firstShot = false;
timeSinceLastShot = System.currentTimeMillis();
}
}
}
Here are the associated methods in my KeyListener class:
public void keyPressed(KeyEvent key) {
int keyPressed = key.getKeyCode();
if (keyPressed == up) {
this.t1.toggleUpPressed();
}
if (keyPressed == down) {
this.t1.toggleDownPressed();
}
if (keyPressed == left) {
this.t1.toggleLeftPressed();
}
if (keyPressed == right) {
this.t1.toggleRightPressed();
}
if(keyPressed == shoot) {
this.t1.toggleShootPressed();
this.t1.setFirstShot(true);
}
I am including this for additional information even though the bug happens before the key is released:
public void keyReleased(KeyEvent ke) {
int keyReleased = ke.getKeyCode();
if (keyReleased == up) {
this.t1.unToggleUpPressed();
}
if (keyReleased == down) {
this.t1.unToggleDownPressed();
}
if (keyReleased == left) {
this.t1.unToggleLeftPressed();
}
if (keyReleased == right) {
this.t1.unToggleRightPressed();
}
if (keyReleased == shoot) {
this.t1.unToggleShootPressed();
}
}
I suspect it's an issue with key-repeats which will repeatedly call KeyPressed while the key is held. It is usually setup to behave as you describe. Consequently, "firstShot" will be repeatedly set to true and a shot will be fired.
I advise restricting your event code, mainly as it's continuous in nature, to only toggling actions, rather than performing any logic. You can determine whether a shot is first or not in your game loop, with the help of some messages from your events.
However, the firstShot variable is not really necessary at all as the time delta will account for it. Removing it would also prevent firing faster than every 500ms by pressing the fire key rapidly, which you may or may not want.
Related
Im trying to get my movement smoother. At the moment when I press the up key it will go up and then when I press the left key it stops and then turns left. I want to make it so when i press right it will do it straight away and have no delay. How could I with my code
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
import java.awt.Image;
import javax.swing.*;
class MyFrame extends JFrame implements KeyListener {
JLabel label;
MyFrame(){
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addKeyListener(this);
ImageIcon imageIcon = new ImageIcon("C:\\Users\\jacob\\Downloads\\player.png");
Image image = imageIcon.getImage();
Image newimg = image.getScaledInstance(150, 150, java.awt.Image.SCALE_SMOOTH);
imageIcon = new ImageIcon(newimg);
label = new JLabel(imageIcon);
this.add(label);
this.setSize(500, 500);
this.setVisible(true);
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP)
label.setLocation(label.getX(), label.getY()-10);
else if (e.getKeyCode() == KeyEvent.VK_DOWN)
label.setLocation(label.getX(), label.getY()+10);
else if (e.getKeyCode() == KeyEvent.VK_LEFT)
label.setLocation(label.getX()-10, label.getY());
else if (e.getKeyCode() == KeyEvent.VK_RIGHT)
label.setLocation(label.getX()+10, label.getY());
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
}
The main problem here is that different operating systems have different times for key-repeat functionality (how often, and how much to accelerate, repeated key press deliveries).
The way to solve this is to maintain state for which keys are currently down or not, and in some timer thread, perform actions if the state is true or not.
For example, (in very rough Java, this won't compile without fixing I think) you want something like this
// mark that the key is down
public void keyPressed(KeyEvent e) {
keyboardState[e.getKeyCode()] = true;
}
// mark that the key has been released
public void keyReleased(KeyEvent e) {
keyboardState[e.getKeyCode()] = false;
}
// this function should be called at some repeatable interval, fast enough where it looks "smooth"
public void calledAtFixedInterval(int delta) {
// we can query definitively if the key is currently down or up, rather than waiting on some event to be delivered at an arbitrary interval
if (keyboardState[KeyEvent.VK_LEFT] == true) {
// your logic here
}
}
This architecture is roughly how most game engines work - they will handle user input using an event-driven architecture, but just maintain a giant map of which keys are currently down or not - that way it's easy and fast to query if a key is pressed deep within the render loop.
A solution I commonly use these days is to separate the game animation thread from the input events (mouse and keyboard), employing loose coupling to communicate between the two.
The game loop can be driven by a java.util.Timer operating at 60 fps. I use this rate as it is pretty common for monitors to refresh at 60 fps, so there are diminishing returns for going at a faster rate.
Some prefer to use a javax.swing.Timer but IDK if that is such a good idea. The EDT (event dispatch thread) can become something of a bottle neck. (The classic Java game programming book: Killer Game Programming in Java compares the two timers and the util Timer comes out the winner.) Arguably, a ScheduledThreadPoolExecutor is more robust than either, though it requires a bit of a learning curve if you haven't used Executors for multi-threading yet. (I prefer using JavaFX, so I use its AnimationTimer.)
The class holding the game loop should have public methods where the keyboard or mouse events that are triggered can simply set a variable or toggle a boolean (or add a variable to the top of a queue). The public input method for the keyboard can have a bit of extra processing to compare the incoming key event with current key state (e.g., a maintained map of key states as described elsewhere on this thread) and set a field for the game-loop thread to read and act upon.
This sort of architecture for a game program adds a great deal of predictability and control, as well as clarifying the logic of the various layers. It also takes good advantage of the multi-threading capabilities of today's processors.
My guess would be to probably make a velocityX and velocityY.
And also, if you want smooth movements. You need a tick method.
Basic tick method for Java Game Development:
public synchronized void start() {
thread = new Thread(this);
thread.start();
running = true;
}
public synchronized void stop() {
try {
thread.join();
}catch(Exception e){
e.printStackTrace();
}
}
public void run() {
long lastTime = System.nanoTime();
double ns =1000000000 / 60.0;
double delta = 0;
long timer = System.currentTimeMillis();
int ticks = 0;
int frames = 0;
while(running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while(delta >= 1) {
tick();
ticks++;
delta--;
}
if(running) {
render();
frames++;
}
if(System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println(ticks + " tps" + " | " + frames + " fps");
frame.setTitle(name + " | " + ticks + " tps" + " " + frames + " fps");
frames = 0;
ticks = 0;
//meanings
//ns = nanoseconds
//amountOfTicks = ticks per second
}
}
stop();
}
The start and stop methods are for the Thread.
You can find easy tutorials on how to do the tick method and fully understand it.
When you have your ticks working properly, you can make your x and y += the velX and velY, just like this:
public void tick() {
y += velY;
x += velX;
}
Making the player update the x and y each tick
And the velX and velY need to react to your KeyInputs so they, y'know, have a value, something like this:
if(key == KeyEvent.VK_W) { tempObject.setVelY(-10); keyDown[0] = true; }
if(key == KeyEvent.VK_S) { tempObject.setVelY(10); keyDown[1] =
true; }
if(key == KeyEvent.VK_D) { tempObject.setVelX(10); keyDown[2] =
true; }
if(key == KeyEvent.VK_A) { tempObject.setVelX(-10); keyDown[3] =
true; }
This code, don't copy and paste it. It has different methods because I am basing this code off a game I made a while ago. So methods are different.
And so when you get that, you are basically done.
setVelY and setVelX look something like this:
public void setVelY(int velY){
this.velY = velY;
}
public void setVelY(int velY){
this.velX = velX;
}
So, conclusion.
If you want smoother animations, you need a tick method, so the x and y update every second.
My code will basically serve nothing if you copy and paste it, but it's just the concept of how to make the movements smooth.
So x and y need to update every second. Making a Runnable tick method is your only way.
I really hope this helps you just a little bit.
Good luck!
:D
I'm very new to Processing and am trying to make a simple program that handles a list of audio clips. When the user scrolls down using the mouse wheel, audio clips are skipped, and when they stop scrolling, an audio clip plays through.
The first step for this seems to be finding a way of converting the numbers returned from MouseWheel, which grow vastly larger or smaller as the mouse wheel is scrolled, into true/false-style values that are returned less frequently (every few seconds).
The code below returns true/false, but does so very quickly. If anybody could help me with saying, "scan for changes every 3 seconds", or similar, I'd be enormously appreciative.
int wheelVal; //reading from mouse wheel
boolean hasScrolled = false; // whether mouse has scrolled
void setup() {
size (750, 500);
println ("ready to scroll");}
void draw() {
background (450);}
void mouseWheel(MouseEvent me)
{ wheelVal = me.getCount();
if (wheelVal > 0) {
hasScrolled = true;
println ("true");}
if (wheelVal < 0) {
hasScrolled = false;
println ("false");}
}
I think you're on the right track, but you can probably actually simplify your logic a bit.
Step 1: Create a hasScrolled variable. Set it to true in the mouseWheel() function.
Step 2: Inside the draw() function, use the millis() function or the frameCount variable to check when 3 seconds has gone by. If so, check the hasScrolled variable, and set it back to false.
Here's a small example:
boolean hasScrolled = false;
void draw(){
background(32);
//180 frames is 3 seconds
if(frameCount % 180 == 0){
if(hasScrolled){
println("scrolled!");
}
}
}
void mouseWheel(MouseEvent me){
hasScrolled = true;
}
First thing i want to do is to apologize for my english, i will try to write as understanable as possible.
Also, i have already tried to search the solution for this problem, but i didnt find one until now..
The problematic part of the code is the following:
//eventmanagement for the bat
scene.setOnKeyPressed (new EventHandler <KeyEvent> () {
public void moveBat(double speed) {
if ((speed > 0) && ((bat.getLayoutX() + bat.getWidth())<scene.getWidth())){
bat.setLayoutX(bat.getLayoutX() + speed);
}
if ((speed < 0) && ((bat.getLayoutX() > 0))){
bat.setLayoutX(bat.getLayoutX() + speed);
}
};
#Override
public void handle(KeyEvent event){
if (event.getCode().toString() == "RIGHT"){
this.moveBat(batVelocity);
}
if (event.getCode().toString() == "LEFT"){
this.moveBat(-batVelocity);
}
}
});
This thing works, but if i press the LEFT key for example, and i dont unpress it, so just let it remain pressed, then the "bat" will move left once, then delay for about 1 second, and then continue moving in the left direction.
I want to have a continuos movement in the left direction for the time the LEFT button remains pressed. Anyone has an idea how to fix this??
Thank you very much for your time and answers!!
Crutz
So, OnKeyPressed fires when a key is first pressed and then every second or so after. If you want something to happen continuously, instead of having it happen when the OnKeyPressed event fires, consider having OnKeyPressed and OnKeyReleased control some sort of boolean keyIsPressed, and using that in a while loop of some sort. So OnKeyPressed would set keyIsPressed=true and OnKeyReleased would do the opposite
So what you wanna do is save your KeyCode to a List and check it in your AnimationTimer:
Fields:
private List<KeyCode> input = new ArrayList<>();
and your listeners:
scene.setOnKeyPressed((KeyEvent event) -> {
input.add(event.getCode());
});
scene.setOnKeyReleased((KeyEvent event) -> {
input.remove(event.getCode());
});
And in your AnimationTimer:
if (input.contains(KeyCode.RIGHT)) {
//do something
}
if (input.contains(KeyCode.LEFT)) {
//do something else
}
This is a follow up to a previous question I had. I have a Battleships game with two boards. When the user clicks on the computer board an action occurs, along these lines:
public void mouseClicked(MouseEvent e)
// Get coordinates of mouse click
if (//Set contains cell) {
/add Cell to set of attacked cells
//Determine if set contains attacked cell.
// If yes, hit, if no, miss.
checkForWinner();
The checkForWinner method determines if the game has been won yet. If it hasn't it calls a nextTurn method which changes the current turn. If the currentTurn is set to Computer, a ComputerMove() method is automatically called.
When that method finishes, it again checksforWinner, changes turn and waits for the user to click on the grid to start the cycle again.
Ideally, I'd like to have sound effects, or at the very least a pause between moves. However, no matter how I use Thread.sleep, or TimerTask, or anything else, I can't get it to function correctly.
If I use a simple Thread.sleep(500) in the CheckforWinner method, or in the ComputerMove method, all that happens is the human's go is delayed for the set amount of time. As soon as his move is executed the computer's move is completed immediately.
I know very little about threads but I assume this is because all the initiation of the bouncing back and forth between methods begins with a method in the mouse listener.
Given the set up of my system, is there a way to implement a delay without radically changing things?
Edit: May as well include the classes:
public void checkForWinner() {
if (human.isDefeated())
JOptionPane.showMessageDialog(null, computer.getName() + " wins!");
else if (computer.isDefeated())
JOptionPane.showMessageDialog(null, human.getName() + " wins!");
else
nextTurn();
}
public void nextTurn() {
if (currentTurn == computer) {
currentTurn = human;
} else {
currentTurn = computer;
computerMove();
}
}
public void computerMove() {
if (UI.currentDifficulty == battleships.UI.difficulty.EASY)
computerEasyMove();
else
computerHardMove();
}
public void computerEasyMove() {
// Bunch of code to pick a square and determine if its a hit or not.
checkForWinner();
}
Ideally, I'd like to have sound effects, or at the very least a pause between moves. However, no matter how I use Thread.sleep, or TimerTask, or anything else, I can't get it to function correctly.
You should be using a Swing Timer. Something like:
Timer timer = new Timer(1000, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
currentTurn = computer;
computerMove();
}
});
timer.setRepeats(false);
timer.start();
I'm working on a pacman clone in java using eclipse and sometimes it appears laggy more specifically the movement of pacman/ghosts is slow, sometimes its fine. Once it has happened while I was running it so it wasn't after I added code and it doesn't seem to be after any specific event in game. I can't find any trigger or produce the lag on purpose
The resource manager shows the same cpu usage(only around 50%)/memory usage . Aswell the FPS seems to be around 200 consistently through lag and during the periods where it is working well.
Does anyone know what this could be?
Is there any information I left out that could be of use?
edit - I am basing movement on a timer is that bad? I will post the movement relevant code below is there a good way of posting the whole code on here?
Timer movement = new Timer(20, new ActionListener()//500 is time in milliseconds between
//calls to actionPerformed as far as I know.
{
public void actionPerformed(ActionEvent arg0)
{
if(movingUp == true)
{
moveUp();
}
else if(movingDown == true)
{
moveDown();
}
else if(movingRight == true)
{
moveRight();
}
else if(movingLeft == true)
{
moveLeft();
}
}
});
public void moveUp()
{
yPos -= 1;
this.rect.y -= 1;
}
public void setDirUp()
{
movingUp = true;
movingDown = false;
movingRight = false;
movingLeft = false;
}
in the main class in public void keyPressed:
if(keyCode == KeyEvent.VK_W)
{
if(pacMan.isUpHittingWall == false)
{
pacMan.setDirUp();
pacMan.isDownHittingWall = false;
pacMan.isRightHittingWall = false;
pacMan.isLeftHittingWall = false;
}
}
edit 2 -Thanks for the help guys. I have the movement using System time now and it seems to have fixed the issue because I implemented it only for pacman at first and the ghosts were still slow. Now there is an issue where moving right and down are much slower than moving left or up The only difference I see is that right and down are both adding and left and up are subtracting. What can I do about this?
The updated code is below.
//updated movement code
public void moveUp(long timePassed)
{
yPos -= vy * timePassed;
this.rect.y -= vy * timePassed;
}
public void moveDown(long timePassed)
{
yPos += vy * timePassed;
this.rect.y += vy * timePassed;
}
public void moveRight(long timePassed)
{
xPos += vx * timePassed;
this.rect.x += vx * timePassed;
}
public void moveLeft(long timePassed)
{
xPos -= vx * timePassed;
this.rect.x -= vx * timePassed;
}
//I passed timePassed through a globalInputObject because my input is handled in public //void keyPressed(KeyEvent e) and I didnt know how else to get timePassed in to the //movement method
//Here is the code in gameLoop()
globalInputObject.isPacManMovingUp(timePassed);
globalInputObject.isPacManMovingDown(timePassed);
globalInputObject.isPacManMovingRight(timePassed);
globalInputObject.isPacManMovingLeft(timePassed);
//This is inside the GlobalInputObject
public void isPacManMovingUp(long timePassed)
{
if(pacMan.movingUp == true)
{
pacMan.moveUp(timePassed);
}
}
public void isPacManMovingDown(long timePassed)
{
if(pacMan.movingDown == true)
{
pacMan.moveDown(timePassed);
}
}
public void isPacManMovingRight(long timePassed)
{
if(pacMan.movingRight == true)
{
pacMan.moveRight(timePassed);
}
}
public void isPacManMovingLeft(long timePassed)
{
if(pacMan.movingLeft == true)
{
pacMan.moveLeft(timePassed);
}
}
Rather than always moving the pacman by a constant distance (1 pixel, it appears) each time the timer runs, you should:
Set the timer to run as fast as possible (e.g. once every millisecond or less). Edit: if you set it too fast, the game may end up actually running slower, you'll have to experiment.
Calculate how much time has passed between each frame using the system clock and move the pacman by an amount proportional to that.
Doing the above will mean that if the system is "laggy," it will simply show fewer frames per second, rather than actually moving everything slower.
As I feared, you're basing the distance moved on the time chunk from the Timer. You shouldn't do this as all timers can be variable and unreliable, especially with small time chunks. Better to base movement on difference in system time. So yes, use a Timer or something to run your "game loop", but know the sprite's position and velocity using doubles, and calculate the distance to move based on velocity vector (math vector not Java Vector) * difference in system time. That way if the timer is delayed by say garbage collection, making the time chunk larger, the distance moved will be correspondingly greater and will look smoother.
You should look into creating a proper "main loop" or "game loop" as some call it. Take a look at the game structure part of this wikipedia article.. Basically those input events are happening\invoked from a separate thread than the main thread and they are directly modifying geometry of in game objects. Instead consider something like this for a main loop:
loop:
process collision detection
process animation (alters geometry of game objects)
process input (more on this later)
any other game specific logic
render screen
your process input could be something like this
if (globalInputObject.movingUp==true) {
hero.y -= 10;
}
if (globalInputObject.movingDown==true) {
hero.y += 10;
}
if (globalInputObject.movingLeft==true) {
hero.x -= 10;
}
if (globalInputObject.movingRight==true) {
hero.x += 10;
}
and your input handler would look something like this:
public void actionPerformed(ActionEvent evt) {
if (evt.button==UP_BUTTON) {
globalInputObject.movingUp=true;
}
if (evt.button==DOWN_BUTTON) {
globalInputObject.movingDown=true;
}
if (evt.button==LEFT_BUTTON) {
globalInputObject.movingLeft=true;
}
if (evt.button==RIGHT_BUTTON) {
globalInputObject.movingRight=true;
}
}
Basically the processing that you're doing in your "extra" threads (input thread) is minimal and therefore doesn't interfere with your main thread. Also, this method has the benefied of easily supporting multiple directions simultaneously (ie: UP+RIGHT = diagonal).
Only super high end games have more than a single thread (if they even need it at all). Dealing with synchronisation in a game is not good for performance.