How to limit player activity? - java

I have a simple game where a player shoots some bullets. However, I want to make it so that the player can only shoot a bullet every x seconds. How would I do this?
I have tried to do it by taking the average time between bullets and saying that if average time is less than one second don't let the space bar (the control for shooting) work. However, I don't know how to make the space bar not work, AND that would mean a player could just not shoot for a wile and then shoot a lot of bullets at once.
The method for shooting looks something like this:
public void keyPressed(KeyEvent e) {
if (!keysDown.contains(e.getKeyCode()))
keysDown.add(new Integer(e.getKeyCode()));
This adds the integer of the key value to an array, which is then read here:
if (keysDown.contains(KeyEvent.VK_SPACE)) {
b = new Bullets(x);
bullCount.add(b);
System.out.println(bullCount.get(0).getY());
System.out.println ("There are " + bullCount.size() + "bullets alive.");
//elapsed = System.nanoTime() - start;
//if ((elapsed / bulletCount) < 1000000000) {
//this is where I would say 'no more bullets shot until
//average time in nanoseconds is more than 1 second!' but I don't know how
//}

Make a global variable ie: long lastShot.
When user shot, check if (System.currentTimeMilis()-lastShot>5000) before you allow him to shoot.
If it is ok to shoot, store the lastShot = System.currentTimeMilis(); and do the real shot. If not, don't allow him to shoot.
Here is an example in pseudo-code:
class SomeClass {
private long lastShot;
public void userPressedShot() {
if (System.currentTimeMillis()-lastShot>5000) {
lastShot = System.currentTimeMillis();
doTheRealShot();
}
// Ignored till 5000 miliseconds from last shot
}
}

One overkill option is to use the Guava's RateLimiter
final RateLimiter rateLimiter = RateLimiter.create(2.0); // rate is "2 permits per second"
while(true) {
rateLimiter.acquire();
shootSomething();
}

For this solution I assume that you use a Player Object.
It's very simple:
1.: add the field lastShot of the type long with the value 0 to your class.
2.: Whenever the player shoots, set lastShot to System.currentTimeMillis()
3.: Now, before you let him shoot, check if System.currentTimeMillis()-lastShot is smaller than your bullet delay in seconds.
This would be a MWE which has to be inserted in your player class:
Just call the Method player.shoot() to let him shoot - or not, if it's not possible.
private long lastShot = 0;
//return value is for you to check if the shot happened
public boolean shoot(){
//Did the last shot happen less than 1 second (1000 milliseconds) ago
if(System.currentTimeMillis()-lastShot <= 1000){
//If yes: return false, the shot can not happen
return false;
}else{
lastShot = System.currentTimeMillis();
//If no: insert your shooting code here to be executed
//return true, the shot happened
return true;
}
}

Try using a timer that resets every second the bullets count :
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
bullCount.clear();
}
}, 0, 1000); // 0 is the delay in milliseconds before it starts and 1000 is the time in milliseconds between successive task executions
Then when a player wants to shoot you just have to look at bullCount's size

Related

Java: Randomly generated numbers only appear in small part of given range

I'm working on a game and I want my enemies to spawn with a delay between 1-5 seconds. My code for that part looks like this:
#Override
public void run() {
try {
while (true) {
Random r = new Random();
int cooldownTime = r.nextInt((5000 - 1000) + 1) + 1000;
long lastSpawn = 0;
long time = System.currentTimeMillis();
if (time > lastSpawn + cooldownTime) {
System.out.println(cooldownTime);
addEnemies();
lastSpawn = time;
}
If I understand nextInt correctly this should spawn enemies 1000-5000 ms apart every time, but my results are really weird and I can't quite figure out why. This is an example of what it would look like if I print cooldownTime.
2523
1190
1095
1061
1168
1119
1052
1159
1071
1076
1000
1394
1249
1070
And so on... It seems that the first enemy is truly spawned randomly and the others are always in the low 1000's. This happens every time. Does anyone know why it's like that? I'm quite lost.
Calling Random r = new Random(); repeatedly is extremely pathological and ruins the statistical properties of the generator. (The results you get are most likely a strong function of your system clock time.)
Do that step once and your results will be far better. (There are also more efficient ways to implement a delay than this - consider sleep - Java compilers are not yet optimising out burn loops.)
Note also that your observations are further complicated by the fact that you are not printing every number drawn.
Not sure if this your exact issue, but you had some problems with the logic of System.currentTimeMillis() and were just looking at the total current time and not the difference between the time.
Additionally you only want to look for a new random value once the if actually executes so you do not generate a Random number each loop iteration, but rather each time an enemy spawns you generate a new one for the next enemy.
Here is the modified code that takes all of this into account:
Random r = new Random();
long time = System.currentTimeMillis();
int cooldownTime = r.nextInt((5000 - 1000) + 1) + 1000;
while (true) {
long timeDifference = System.currentTimeMillis() - time;
if (timeDifference > cooldownTime) {
//addEnemies();
System.out.println(timeDifference); //Prints the time taken for enemy to spawn
cooldownTime = r.nextInt((5000 - 1000) + 1) + 1000;
time = System.currentTimeMillis(); //set the initial time again
}
}
This will generate a random number between 1000 and 5000 and execute the if block each time after the delay, resetting the values in the if to do it forever.
However, there are most likely better ways to add a delay to your logic like some comments pointed out(sleep), but this is the corrected logic for your method.
Well, first of all, several of your variables are being created INSIDE the loop. You need to move the creation of the Random object, as well as the lastSpawn and cooldown variables outside the loop. This is because the lastSpawn variable is being overwritten each time the loop executes with 0, meaning you're always checking if the current time is greater than 0. You need to store it outside the loop so that it will retain the last value you assigned to it. For the same reason, cooldown needs to be outside the loop because you're generating a new cooldown every loop, and System.currentTimeMillis() is ALWAYS going to be larger than it because System.currentTimeMillis() gets you the system time offset from January 1, 1970. Finally, as System.currentTimeMillis() is represented by a long, you'll want any time-related variables to be long as well, otherwise you could end up overflowing your variable if the current time in milliseconds is too high for an integer to store.
Here is a better way to achieve what you're looking to do:
import java.util.Random;
public class RandomCooldown {
public static void main(String [] args) {
Random rand = new Random();
long start = System.currentTimeMillis();
long lastSpawn = start;
long cooldown = getCooldown(rand);
while(true) {
long time = System.currentTimeMillis();
long elapsed = (time - lastSpawn);
if(elapsed >= cooldown) {
System.out.println("Adding enemies!");
cooldown = getCooldown(rand); // only generate a new cooldown once the old cooldown has been surpassed
lastSpawn = time;
}
}
}
public static long getCooldown(Random rand) {
return (long)((rand.nextInt(4000) + 1) + 1000);
}
}
Hope this helps!
Here is a suggestion that works as I think you like. I has not one but two loops. The outer loop generates new enemies and the inner loop makes updates (not sure how much of that this enemy generating thread needs to update but included it just in case).
public void run() {
Random r = new Random();
// setup
while (true) {
int wait = r.nextInt((5000 - 1000) + 1) + 1000;
long time = System.currentTimeMillis();
System.out.println("Adding enemies at " + time
+ ", next add roughly in " + wait + " ms.");
while (wait + time > System.currentTimeMillis()) {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
// update
}
}
}
The Random object is reused and the call Thread.sleep(30) makes the thread hand over control for 30 ms, a time during which the CPU can do more useful things than busy wait. (30 is BTW not a magic number that must be used but just a number I chose. You need to experiment and find what number works best in your game.)
The print out shows what happens.
Adding enemies at 1565096018610, next add roughly in 2890 ms.
Adding enemies at 1565096021530, next add roughly in 2301 ms.
Adding enemies at 1565096023863, next add roughly in 4944 ms.
Adding enemies at 1565096028813, next add roughly in 3042 ms.
Adding enemies at 1565096031879, next add roughly in 2661 ms.
... and so on. The actual numbers will not be the same of course when you run this code but similar.
Hope this helps and good luck with your game!
It looks fine to me. You should probably use the same Random() instance for each iteration. And remember: humans have no ability to perceive randomness. Alternatively, you could try seeding the Random-Generator (using the Random(long seed) constructor), just in case there's some weird stuff happening with your seed.

Double (chess like) timer

I am thinking about making an android app to support a game of mine, and I can't find info on how to do it. Here is an app explanation:
The app should have two timers (both set for x minutes). When first player starts his turn (by a button click) his time starts running out. when first player makes his move, he should click his time to stop it, and player two starts his turn.
NOW, the problem I can't find a solution for - When player one takes his turn, time that he loses should pass to the other player's timer.
Example: Both players start with a 5 minute timer. If player one takes 30 second for his turn, his time should go down to 04:30, at which time he clicked on his timer. During that time, for each second he lost, the other player gained time on his timer, so at the beginning of his turn, his time is 05:30. The time goes back and forth, and the player whose timer runs out loses the game.
Any idea how to do this?
I am still stuck to the idea on how to make it, so I have no code to share.
Thank you all for your answers, and effort in helping me, if you have a question I haven't covered, I will gladly answer it.
First, you will need to instantiate class of CountDownTimer everytime you click on button.
For reference check here: https://developer.android.com/reference/android/os/CountDownTimer.html
You have two parameters long millisInFuture, long countDownInterval
Before you start, you will need to create two global variables one for first player, and one for second player like this:
long firstPlayerRemainingTime = 5 * 60 * 1000; // start time 5 minutes
long secondPlayerRemainingTime = 5 * 60 * 1000;
long limitedTime = 30 * 1000; // 30 seconds
CountDownTimer mCountDownTimer;
Now we have came to the most important part and that is logic inside onClickListener method
I don't know if there are two buttons or one, but I will go with two buttons:
btnFirstPlayer.setTag(1); // start timer
btnFirstPlayer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (btnFirstPlayer.getTag() == 1) {
startTimer();
} else {
stopTimer();
}
});
private void startTimer() {
long startTime = firstPlayerRemainingTime;
btnFirstPlayer.setTag(2); // stop timer
btnFirstPlayer.setText("Stop");
mCountDownTimer = new CountDownTimer(firstPlayerRemainingTime, 1000) {
public void onTick(long millisUntilFinished) {
firstPlayerRemainingTime = millisUntilFinished;
tvPlayerOneTimer.setText("" + firstPlayerRemainingTime / 1000)
// Here you would like to check if 30 seconds has passed
if ((startTime / 1000) - (limitedTime / 1000)
== (firstPlayerRemainingTime / 1000)) {
stopTimer();
}
// Here you would like to increase the time of the second player
secondPlayerRemainingTime = ++1000;
}
public void onFinish() {
mTextField.setText("done!");
}
}.start();
}
}
private void stopTimer() {
btnFirstPlayer.setTag(1);
btnFirstPlayer.setText("Start");
mCountDownTimer.cancel();
// I guess here starts second player move
}
The same logic would go for second player. Let me know if this helped you or if I need to explain anything.

How to delay a method's repeated execution?

I'm new to java and I'm trying to constantly add "zombie" entity to my game after a delay, and then shortening the delay as time goes on.
In my PlayState class I've got the code to spawn the entities in a position when my update(); method is run (which houses getInput(); and such. Not shown for clarity.)
public void update(long elapsed) {
this.entities.add(new Zombie(-535));
}
How would i make a delay that shortens? I'm guessing I would make the delay and then use a multiplier which i have getting smaller every 10 seconds or so, but how would I do this?
Now, I don't know much about the finer workings of your class, but this might give you a general idea of what I mean:
int counter = 50;
for(int i = 100; i >= 0; i--)
{
if(i == counter)
{
counter = counter / 2;
this.entities.add(new Zombie(-535));
}
}
Suppose i is the the total run-time of the game, and counter represents a percent of the game when you want to add a zombie.
If you want to add a zombie after 50% of the run-time (here, 100 seconds), then as the time reduces, you check if the time has come to add a zombie (Here, 50 seconds).
What I've done here is reduce the delay to half, and continue checking if the time has come to add a zombie.
Maybe you could call sleep on your thread of execution:
int sleepInMs = 5000
Thread.sleep(sleepInMs);
sleepInMs+=1000; //Then of course handle the case when sleepInMs == 0
Really need more information about your implementation.
For a simple delay, use "delay ms;"
Edit ms for the number of milliseconds you want. 1000 milliseconds is one second

set a delay in libgdx game

i have a game(like super jumper, this game is a jumping game) that our character has life. after collision with enemies, his life reduce. and i want to after 1 sec , calculate the collisions. i mean in this 1 sec, if my character contact with enemies , nothing happen and he continue his way.
for this , i define a boolean variable in my GameScreen class, name "collision" and another in Wolrd class, name "collBirds". after one contact with enemy collision and collBirds change to true. but i want after 1 sec collistion change to false. i use several things like System.currentTimeMillis() and "for loop",and nothing happen. i'm not so good in java.
this is my condition:
if(World.collBirds == true && collition == false){
life -= 1;
lifeString = "Life : " + life;
World.collBirds = false;
collition = true;
for (??? "need to stay here for 1 sec" ???) {
collition = false;
}
}
In some cases you could also want to use com.badlogic.gdx.utils.Timer
Example usage:
float delay = 1; // seconds
Timer.schedule(new Task(){
#Override
public void run() {
// Do your work
}
}, delay);
When the first collision occurs, set a float timeSinceCollision = 0;
Then each loop, you will need to add the time since last check to the variable, and check if it's more than a second.
timeSinceCollision += deltaTime;
if(timeSinceCollision > 1.0f) {
// do collision stuff
} else {
// ignore the collision
}
If you want to do this in same thread than you can use Thread.sleep(). But in this case your current thread will freeze and if this is single thread game, than your whole game will freeze. If you don't want your game to freeze for 1 second, than you should spawn the thread and in that thread call the sleep and after sleep, change the flag

read array in certain time range

How can I read an array in java in a certain time? Lets say in 1000 milliseconds.
for example:
float e[]=new float [512];
float step = 1000.0 / e.length; // I guess we need something like that?
for(int i=0; i<e.length; i++){
}
You'd need a Timer. Take a look at its methods... There's a number of them, but they can be divided into two categories: those that schedule at a fixed delay (the schedule(... methods) and those that schedule at a fixed rate (the scheduleAtFixedRate(... methods).
A fixed delay is what you want if you require "smoothness". That means, the time in between executions of the task is mostly constant. This would be the sort of thing you'd require for an animation in a game, where it's okay if one execution might lag behind a bit as long as the average delay is around your target time.
A fixed rate is what you want if you require the task's executions to amount to a total time. In other words, the average time over all executions must be constant. If some executions are delayed, multiple ones can then be run afterwards to "catch up". This is different from fixed delay where a task won't be run sooner just because one might have "missed" its cue.
I'd reckon fixed rate is what you're after. So you'd need to create a new Timer first. Then you'd need to call method scheduleAtFixedRate(TimerTask task, long delay, long period). That second argument can be 0 if you wish the timer to start immediately. The third argument should be the time in between task runs. In your case, if you want the total time to be 1000 milliseconds, it'd be 1000/array size. Not array size/1000 as you did.
That leaves us with the first argument: a TimerTask. Notice that this is an abstract class, which requires only the run() method to be implemented. So you'll need to make a subclass and implement that method. Since you're operating over an array, you'll need to supply that array to your implementation, via a constructor. You could then keep an index of which element was last processed and increment that each time run() is called. Basically, you're replacing the for loop by a run() method with a counter. Obviously, you should no longer do anything if the counter has reached the last element. In that case, you can set some (boolean) flag in your TimerTask implementation that indicates the last element was processed.
After creating your TimerTask and scheduling it on a Timer, you'll need to wait for the TimerTask's flag to be set, indicating it has done its work. Then you can call cancel() on the Timer to stop it. Otherwise it's gonna keep calling useless run() methods on the task.
Do keep the following in mind: if the work done in the run() method typically takes longer than the interval between two executions, which in your case would be around 2 milliseconds, this isn't gonna work very well. It only makes sense to do this if the for loop would normally take less than 1 second to complete. Preferably much less.
EDIT: oh, also won't work well if the array size gets too close to the time limit. If you want 1000 milliseconds and you have 2000 array elements, you'll end up passing in 0 for the period argument due to rounding. In that case you might as well run the for loop.
EDIT 2: ah why not...
import java.util.Random;
import java.util.Timer;
public class LoopTest {
private final static long desiredTime = 1000;
public static void main(String[] args) {
final float[] input = new float[512];
final Random rand = new Random();
for(int i = 0; i < input.length; ++i) {
input[i] = rand.nextFloat();
}
final Timer timer = new Timer();
final LoopTask task = new LoopTask(input);
double interval = ((double)desiredTime/((double)input.length));
long period = (long)Math.ceil(interval);
final long t1 = System.currentTimeMillis();
timer.scheduleAtFixedRate(task, 0, period);
while(!task.isDone()) {
try {
Thread.sleep(50);
} catch(final InterruptedException i) {
//Meh
}
}
final long t2 = System.currentTimeMillis();
timer.cancel();
System.out.println("Ended up taking " + (t2 - t1) + " ms");
}
}
import java.util.TimerTask;
public class LoopTask extends TimerTask {
private final float[] input;
private int index = 0;
private boolean done = false;
public LoopTask(final float[] input) {
this.input = input;
}
#Override
public void run() {
if(index == input.length) {
done = true;
} else {
//TODO: actual processing goes here
System.out.println("Element " + index + ": " + input[index]);
++index;
}
}
public boolean isDone() {
return done;
}
}
Change your step to be time per number (or total time divided by number of steps)
float step = 1000.0 / e.length;
Inside your for() loop:
try{
Thread.sleep(step);
}catch(InterruptedException e){
e.printStackTrace();
}

Categories