I' m currently writing a simple 2D game in java from scratch (for learning purposes)
I want to control the rate at which a player can shoot. The method done there works, but it could be improved. The method is getting called if the user presses/holds the left mouse button. It works when the user is holding the button pressed, but when he/she releases the mouse button, waits (more then the rateOfFire time) and tries to shoot it may or may not work, because the roftC value isn' t getting updated when the player doesn' t shoot.
I tried then to put it into my update() method (which gets called 60 times a second). The problem still exists. I really have no idea how to solve this. Here is my code:
/**
* Used to control the rate of fire
*/
private int roftC = 0;
/**
* Shoot a Projectile
*/
protected void shoot(int x, int y, double dir) {
Projectile p = new Bomb(x, y, dir);
if (roftC % p.getRateOfFire() == 0) {
level.addProjectile(p);
}
if (roftC > 6000) {
roftC = 0;
}
roftC++; // Whether it is here or down there doesn' t make a diffrence
}
/**
*
*/
#Override
public void update() {
// roftC++;
}
One idea would be to introduce a minimum delay between shots. Something like this:
static final long MINIMUM_DELAY = 1000/30; // So we can have 30 shots per second
long lastShotTimestamp;
protected void shoot(int x, int y, double dir) {
long now = System.currentTimeMillis();
if (now - lastShotTimestamp > MINIMUM_DELAY) {
level.addProjectile(new Bomb(x, y, dir));
lastShotTimestamp = now;
}
}
Such an approach is actually close to the physics - a gun needs some time to reload between consecutive shots.
Related
When I click screen, the Stop screen flashes on and then it goes back to the Walk screen. How do I get it to stay on the Stop screen until I click my mouse again?
boolean walk;
String textStop;
String textWalk;
float x; //positions
float y; //positions
void setup() {
size (400,500);
stop = false;
textStop = "STOP!";
textWalk = "WALK!";
}
void draw() {
background(0,255,0);
text(textWalk, x , y);
x = 150;
y = 250;
textSize(40);
}
void mousePressed() {
if (stop);{
background(255,0,0);
text(textStop, x , y);
}
} ```
Whenever you call background() you're clearing the screen.
Because this happens in draw() every frame is cleared and redrawn with textWalk (and textStop) only gets displayed when you click (for the short duration of the mousePressed() event).
You could use the walk variable for that. I see you use stop though it's not declared. They seem to be the opposite of each other in context so why not simpify and use just one. Sure you can code this if/else to toggle the value or you can simply use the !(logical not) operator:
void mousePressed() {
walk = !walk;
}
when walk is true render textWalk
otherwise (else) render textStop
something like:
if(walk){
text(textWalk, x , y);
}else{
text(textStop, x , y);
}
Here's a modified version of your code using the notes above:
boolean walk = true;
String textStop = "STOP!";
String textWalk = "WALK!";
float x = 150; //positions
float y = 250; //positions
void setup() {
size (400,500);
textSize(40);
}
void draw() {
if(walk){
background(0,255,0);
text(textWalk, x , y);
}else{
background(255,0,0);
text(textStop, x , y);
}
}
void mousePressed() {
walk = !walk;
}
(Notice I've moved a few lines: in general it's a good idea to initialise values that don't change once at the start of your program rather than continuously. In this basic example it doesn't really affect performance, but for more complex programs it's a good idea to keep track of what changes and what doesn't.
Also, it might help with other programs in the future to have a bit of separation between data that changes on events (e.g. walk on mouse pressed) and how that data gets rendered (e.g. using walk to render in draw())
I have this update method shown below:
#Override
public void update(Input input, int delta) {
/* if UP, move player 5 tiles upwards */
if (input.isKeyPressed(Input.KEY_UP) {
y -= 5;
setY(y);
}
/* if DOWN, move player 5 tiles downwards */
if (input.isKeyPressed(Input.KEY_DOWN) {
y += 5;
setY(y);
}
/* if LEFT, move player 5 tiles to the left */
if (input.isKeyPressed(Input.KEY_LEFT) {
x -= 5;
setX(x);
}
/* if RIGHT, move player 5 tiles to the right */
if (input.isKeyPressed(Input.KEY_RIGHT) {
x += 5;
setX(x);
}
}
My update loop from my World class:
public void update(Input input, int delta)
throws SlickException {
// Update every sprite eg. Player, Blocks etc.
for (Sprite sprite : list) {
sprite.update(input, delta);
}
}
Where setX() and setY() are just setters from my class, which handle how many pixels the player should move in terms of tiles. Where each tile is 32 pixels.
So far this moves my player from one location to another location 5 tiles down, up, left or right. I was wondering if their was a way to make the player move one tile every 0.25 seconds to its destination? As in, every 0.25 seconds, the player would move 32 pixels in the left, right, down or up direction. I want this to be added so it looks like the player is sliding across tiles instead of teleporting straight to its location.
How could I use a timer to achieve this? Could I use delta to do this? Any sort of help would be appreciated.
See this answer:
Java slick2d moving an object every x seconds
You should definitly use delta for this since you want framerate independent motion in your game.
Keep a variable outside of the update loop that contains a timestamp.
int lastTimestamp;
Now, in your loop, make a conditional that checks if 250 milliseconds has passed since the time in lastTimetamp.
// Use System.nanoTime() / 1000000L to get current time
if(currentTime >= (lastTimestamp + 250)) {
movePlayer();
lastTimestamp = currentTime;
}
Now this condition should pass every 0.25 seconds.
I'd add a Runnable class MoveAnimation that you give to a
ScheduledExecutorService.
class MoveAnimation { // the full player movement
public boolean animationFinished();
public void animate() {
if (animationFinished()) {
throw new Exception(); // there is probably a cleaner way to remove this from the executor
}
}
}
class AnimationControl {
private final ScheduledExecutorService animationSchedule = Executors.newScheduledThreadPool(1);
public void addMovement(MoveAnimation animation) {
animationSchedule.scheduleAtFixedRate(animation::animate, 0, 250, TimeUnit.MILLISECONDS);
}
}
// in the event listener
if (input.keyIsPressed(Input.KEY_LEFT)) {
animationControl.addMovement(new MoveLeftAnimation(from, to));
}
Basically what I just do to create a timer is float timer += 1 * deltaTime and it will add 1 every second, but I'm having a problem on using it.
if(timer < 2){
//do something
}
I want the if statement to stop running the code when timer is at 2 seconds, buy since I cannot do if(timer != 2f) because it will not even detect 2 seconds because it's too fast. This means I have to put a condition timer < 2f, which is not accurate and always gives me inacurate results.
Instead of using a hand made timer why not use a Task and a boolean?
Pseudo code below;
boolean stop = false;
Timer.schedule(new Task() {
#Override
public void run() {
stop = true;
}
}, 2f);
Then in your render method;
render() {
if (!stop) {
// Freefall
}
}
If I understand your question correctly, you want your code to process for X seconds and no more (where X can be a floating point value such as 12.556 seconds).
I'm going to propose a single-threaded alternative where you have a custom Timer class that manages your freefall logic as shown below. It watches the progress and if it sees something greater than the duration, it "lies" to the internal logic so that your logic is only executed for the amount of time specified (within a configurable margin of error since we're playing with floats).
The example below demonstrates this sort of thing without using libGDX (just in case folks using other libraries are interested), but it would be trivial to swap out my simulated getDeltaTime() method with Gdx.graphics.getDeltaTime().
package tech.otter.timing;
/**
* Created by john on 11/20/16.
*/
public class TimingExample {
public static void main(String... args) {
boolean complete = false;
Timer t = new Timer(15f) {
// You could implement your logic here.
#Override
void action(float delta) {
System.out.println(progress);
}
};
while(!complete) {
complete = t.update(getDeltaTime());
}
assert t.progress < t.duration;
assert t.progress + t.errorMargin > t.duration;
}
/**
* Simulates processing time by returning 0-100ms.
* #return The number of milliseconds that have allegedly elapsed since the last call.
*/
public static float getDeltaTime() {
return (float)(Math.random() / 10);
}
abstract static class Timer {
private float duration;
protected float progress;
private float errorMargin;
public Timer(float duration) {
this(duration, 0.0001f);
}
public Timer(float duration, float errorMargin) {
this.duration = duration;
this.errorMargin = errorMargin;
this.progress = 0f;
}
/**
* Update the timer based on how long since the last call.
* #param delta The amount of time since the last call.
* #return Whether the timer's progressed has met the duration.
*/
public boolean update(float delta) {
// This if-statement "caps" the delta so that we will never exceed the duration.
if(progress + delta > duration) {
delta = duration - progress;
}
progress += delta;
action(delta);
// Return "true" if the progress is equal to the duration (+/- a small margin just in case since we use floats).
return progress + errorMargin > duration && progress - errorMargin < duration;
}
/**
* Override this method with your game logic.
* You should not call it directly.
* #param delta The amount of time that has elapsed since the timer was last updated.
*/
abstract void action(float delta);
}
}
I'm creating just a simple 2D game, and I'm having troubles understanding how to interact with other classes.
For example, before now I had all of the player pictures and coordinates stored inside the main loop of the program, but now I wish to take it outside of that, into a new class. and just refer to the player as like Player.whateverhere
To change it.. e.g OO programming.
GitHub Link for my full code.
public int x, y;
public boolean up, down, left, right, jump;
public Image offScreen;
public Graphics d;
public BufferedImage walkAnimations[] = new BufferedImage[5];
public BufferedImage background;
public BufferedImage player;
public void run() {
// Set Window Size
x = 100;
y = 100;
try {
// Import the images to use in the game
background = ImageIO.read(new File("img/background.jpg"));
walkAnimations[0] = ImageIO.read(new File("img/walkleft.png"));
walkAnimations[1] = ImageIO.read(new File("img/walkright.png"));
walkAnimations[2] = ImageIO.read(new File("img/walkup.png"));
walkAnimations[3] = ImageIO.read(new File("img/walkdown.png"));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
player = walkAnimations[3];
while (true) {
// Create the Gravity (If player !jumping; and if player is below or above the ground, then set player to ground.
if (!jump) {
if (y < Character.GROUND_LEVEL || y > Character.GROUND_LEVEL) {
y = Character.GROUND_LEVEL;
}
}
// If Keys are Pressed, Move the object
if (left) {
x -= 2;
} else if (right) {
x += 2;
} else if (up) {
y -= 2;
} else if (down) {
y += 2;
}
// Repaint the window to update the changes
repaint();
try {
// Make the thread sleep
Thread.sleep(20);
} catch (InterruptedException e) {
// Catch any errors with the Thread
e.printStackTrace();
}
}
}
(For the people not wanting to go to the trouble of visiting my GitHub for the code); i basically want to take the x/y, and images and stuff, and put them in a new class called "Player" but having a hard time making it work (e.g by doing this.player.y or player.y)
Simply put, if you look at Alpha 0.003 update, you can see it all in 2 classes.
I've tried to move all of the player stuff, into a new class/package called Player
So i'd like to be able to refer to it as say: player.x = 0; instead of doing x = 0;
and same with setting animations.
I'd like to just do this.player = walkAnimations[0]
But I don't understand how to do it properly. Is doing Player player = new Player(); wrong?
Basically, what you're doing is evil. Not "I murdered a baby and drank its blood evil", just the sort of affable friendly evil, like "I went to go pee during the commercial break".
So to answer the question, how should objects interact?
They should interact by sending each other messages.
In Java™ you interact by calling a method.
For instance, from a recent obligatory assignment me and another TA made, we have a SimplePlayer class with an illustrative example:
#Override
public void setDirection(Direction dir) {
this.faces = dir;
}
Thus if I want to tell the player object to face south, I can send it a message telling it to face south:
player.setDirection(Direction.SOUTH);
Now, you could ask, why not just call it like this?
p.faces = Direction.SOUTH;
Wouldn't that work?
Yes, in this and only this instance.
However, there might be other problems afoot when doing that sort of thing. Maybe the player object has to do something when he turns. Maybe it has to tell the sound system to play some noise. Maybe it has to add a number somewhere keeping track of how many steps you've taken. Maybe it has to send an email to the NSA. Who knows. And when you send the message, you don't have to care. the Player object is responsible for all of that. You are not.
This is the answer to the question "how should I interact with my objects?"
The answer to "Why doesn't what I do work?" is that the types don't match. You're trying to make the player be a BufferedImage object, which is not what you want.
"this.player = walkAnimations[0]"
this.player is a Player type variable.
walkAnimations[0] is a cell of a BufferedImage type array.
Therefore, the line will not compile.
Your question wasn't very clear, but I'll try to give you general guidelines on how to approach OO game programming, since I think essentially that's what your asking.
The key thing in Object Oriented Programming is that entities are represented by classes, and classes hold both the entity's state (the data related with it), and it's operations.
So for example a car can be represented by a Car class. This class will contain what characterizes a car - it's weight, it's color, it's price, etc - and also will contain what it can do - start, drive, stop, etc.
That means that every entity in your game (be it the player, monsters, whatever) should have it's own class. And that class should contain all of the entity's info - e.g. it's position on the screen (x,y), it's image - and also anything it can do (e.g. move on the screen, jump, fire a missile).
Here is a very simple Entity class for example in java-pseudocode. All game entity classes should inherit from this class:
class Entity{
private int x, y; // position on screen
private int dx, dy; // this is how much the entity moves each game-loop cycle.
private Image image;
private Rectangle boundingBox;
private int healthPoints;
public Entity(int x, int y){
// ths position of the thing is set through the constructor
this.x = x;
this.y = y;
dx = 0;
dy = 0;
healthPoints = 100;
Image = new Image(/*some file*/); // this is pseudo code.
boundingBox = new Rectangle(image); // pseudo code. A rectangle the size of the image.
}
public int getX(){ return x; }
public int getY() { return y; }
public Image getImage() { return image; }
// user input can affect this from the outside, to move the entity. or possibly an ai mechanism.
public void setDX(int dx){ this.dx = dx; }
public void setDY(int dy){ this.dy = dy; }
public void move(){
x += dx;
y += dy;
}
public Rectangle getBoundingBox(){ return boundingBox; }
}
Any object derived from this class will hold for itself all of it's data and operations.
Now, your gameloop control manages the entities from the outside and access their data - to move them, check for collisions, display their image, etc.
For example, a very simplified game class holding the game loop. In pseudocode (this is one approach, but it's good to start with something similar):
Class GameClass{
ArrayList entities;
public GameClass(){
entities = new ArrayList();
startGameLoop();
}
private void startGameLoop(){
while(true){ // this is bad, don't do this.
displayEntities(); // step 1 of gameloop.
moveEntities(); // step 2
checkForCollisions(); // step 3
}
}
private void displayEntities(){
for(int i=0; i<entities.size(); i++){
Entity entity = entities.get(i);
displayImage(entity.getImage(),entity.getX(),entity.getY());
}
}
private void updateEntities(){
for(int i=0; i<entities.size(); i++){
Entity entity = entities.get(i);
entity.move();
}
}
private void checkForCollisions(){
for(int i=0; i<entities.size(); i++){
Entity entity = entities.get(i);
for(int j=0; j<entities.size(); j++){
entities.remove(i);
}
}
}
}
This is missing ofcourse logic to get user input. What you can do is for example when the user presses the right arrow, set character.setDX(2), and left arrow will be character.setDX(-2). Remember that dx is the value added to x every cycle, when move() is invoked. This is one approach.
It's also missing tons of other things.
It's impossible to cover all of the basics in this one post. But just remember one thing:
All entities are encapsulated in classes, that hold their own data and operations. Have a central class to manage it all, contating a game loop. Also classes can have methods to interact with other classes.
Hope this helps.
EDIT: Also, here's a series of tutorials that helped me a lot when I was starting out.
I have the need at my workstation to disable a long idle.
I've been using a piece of code that moves the mouse on the screen once a while, which worked pretty well.
Lately, our security department applied a smart-card&biometric login policy,
which requires my card to be present inside the keyboard (special slot).
Since then the troubles began.
The process works fine as long as I'm logged in, meaning my card is present, but not working if my card is removed.
(I've been logging the process activity, and everything works fine, but the mouse cursor doesn't move.)
Can someone suggest me a way (or link) so I can solve this matter?
Edit
OS: Windows XP
Here's the code that is resposible for moving the mouse.
Notice that I have logged its operation, and tested it: The results outcome logged entries (as if the code was ran) but the mouse cursor wasn't moved on the locked session.
Thanks again
package AntiIdle;
import java.awt.*;
import java.util.Calendar;
import java.text.SimpleDateFormat;
class SimulateMouseAction implements Runnable {
/**
* Robot object used to move the mouse.
*/
private Robot iRobot;
/**
* Bed object, so thread could notify sleeping objects.
*/
private Bed bed;
/**
* Default constructor is neutralized.
*/
#SuppressWarnings("unused")
private SimulateMouseAction () { }
/**
* Constructs class with bed object.
*
* #param bed Bed object to notify the sleepers.
* #throws AWTException if Robot creation fails.
*/
public SimulateMouseAction (Bed bed) throws AWTException {
this.bed = bed;
iRobot = new Robot();
}
/**
* Activates tread.
*/
public void run() {
System.out.println(new SimpleDateFormat("d/M/yy hh:mm").format(Calendar.getInstance().getTime()) +
"Mouse start");
Dimension scrSize = Toolkit.getDefaultToolkit().getScreenSize();
int x, y;
for (int prcnt=0; prcnt<100; prcnt++) {
x = (int) scrSize.getWidth() * prcnt / 100;
y = (int) scrSize.getHeight() * prcnt / 100;
moveMouse(x, y);
}
y = (int) scrSize.getHeight() - 20;
for (int prcnt=100; prcnt>0; prcnt--) {
x = (int) scrSize.getWidth() * prcnt / 100;
moveMouse(x, y);
}
for (int prcnt=0; prcnt<100; prcnt++) {
x = (int) scrSize.getWidth() * prcnt / 100;
y = (int) scrSize.getHeight() * (100-prcnt) / 100;
moveMouse(x, y);
}
iRobot.mouseMove((int) scrSize.getWidth()/2, (int) scrSize.getHeight()/2);
System.out.println(new SimpleDateFormat("d/M/yy hh:mm").format(Calendar.getInstance().getTime()) +
"Mouse end");
bed.awakeTheSleepers();
}
/**
* Moves mouse cursor to given coordinates.
*
* #param x X coordinate.
* #param y Y coordinate.
*/
private void moveMouse(int x, int y) {
iRobot.mouseMove(x, y);
try {
Thread.sleep(10);
} catch(InterruptedException ie) {
ie.printStackTrace();
}
}
}
Edit
#Mark Peters: Thanks for repling. As funny as it seems, they gave me this solution... I've built a java application which launches some vb script that loads Excel and performs some tasks and create reports. The problem is that it stops launching Excel after a day of idle on my station. Everything else seem to be working (The java application keeps working-I've been logging its activity as well as the mouse mover activity in the attached code above). So as you can see, the security problem you suggested isn't solved either way. As a rule at my country, I'm forced to have 1 week of vacation in a row at least once a year. Ironically, this what's preventing me from fulfilling this duty.
Any suggestion?