I am trying to make a simple game using libgdx. One thing that I am stuck with is making enemies spawn at specific times. If I do something like
if (t == 10)
new Enemy();
I might miss this specific time or maybe spawn the same enemy twice. What I have right now is something like
float t = 0
float timeElapsed = 0;
update (float delta) {
timeElapsed += getDeltaTime();
if (timeElapsed > 0.1) {
t++;
timeElapsed = 0;
}
}
This gives me the approximate elapsed time in tenths of seconds for t, but it really doesn't feel like the way I should be doing this.
I have a solution I use in my games that might be useful. I actually created a Timer class:
public class Timer
{
protected float remaining;
protected float interval;
public Timer(float interval)
{
this.interval = interval;
this.remaining = interval;
}
public boolean hasTimeElapsed() { return (remaining < 0.0F); }
public void reset() { remaining = interval; }
public void reset(float interval) {
this.interval = interval;
this.remaining = interval;
}
public void update(float delta) { remaining -= delta; }
}
You initialize the Timer to a certain time period, then in your update(delta) method you call Timer.update(delta) on all your Timers, then check if any of the timers have elapsed by calling Timer.hasTimeElapsed().
In your case, you only need one Timer object, since the enemies are spawned in sequence. Once you spawn an enemy, you reset the Timer (changing the spawn period if you want) and wait for it to go off again.
You can also modify the Timer object to use the subject-observer pattern in order to trigger callbacks when a timer goes off. This is useful if you have logic that needs to know when a timed event occurs, but the logic does not have direct access to the delta time.
Also, if you have a slow frame with eg. getDeltaTime() = 0.2, the enemy's spawn will be delayed.
The simplest way that comes to mind is to get rid of t - compare directly against timeElapsed, and keep track of the object references to know whether you've spawned each enemy. ie.
if (enemy1 == NULL && elapsedTime > 10) {
enemy1 = new Enemy();
}
if (enemy2 == NULL && elapsedTime > 30) {
enemy2 = new Enemy();
}
For a more scalable approach, you could create a linked list of spawn times, and when you spawn an enemy advance the list pointer. That way you only have to compare against one time (the spawn-time on the current list node) per frame.
Addendum: it's rarely a good idea to use == in the context of floating point numbers. See http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html for gory details.
Why do you increment t and then you spawn enemies based on t value. Why don't you do it like this ?
final float step = 0.1f;
float timeElapsed = 0f;
update (float delta) {
timeElapsed += delta;
while (timeElapsed > step){
timeElapsed-=step;
createEnemy();
}
}
With this approach, when your game lags and you get delta lets say 0.5f and you step is 0.1, you will create 5 enemies. I don't know if you want or don't want this beviour
Related
May be this will be a very basic question,but as a newbie,I am confused of it.
In my libGDX Project,I wants to move the player.
Initially player is on left side.For the first tap player should move to opposite side and occupy the position there.for the next tap it should move to left.and will continue like this.
I implemented this logic.Now problem is that my player is not at all moving but just occupying the opposite position on tap.I used a velocity value but it does not make any effect on the code.
I want to make it move along the way,not just occupying the position.
Please help.
public float ninjaX = Constants.W_WIDTH;
public float ninjaY =Constants.WORLD_HEIGHT/2+Constants.WORLD_HEIGHT/4;
public float ninjaVelocity =100f;
public boolean isLeftBool=true;
public void ninjaMove() {
if (isLeftBool) {
ninjaX = ninjaX+ ninjaVelocity;
setPosition(Constants.WORLD_WIDTH - (Constants.W_WIDTH+Constants.PLAYER_HEIGHT/2), ninjaY);
isLeftBool = false;
}
else
{
ninjaX=ninjaX-ninjaVelocity;
setPosition(ninjaX,ninjaY);
isLeftBool = true;
}
}
updating player in render:
if (MyInputProcessor.isTap) {
MyInputProcessor.isTap = false;
ninja.ninjaMove();
}
Your player coordinates changes should depend on delta time:
x1 = x0 + v * Δt
x1 - is new coordinate
x0 - is current coordinate
v - speed
Δt - time span between x0 and x1
LibGDX provides the method to get the time span between current and last frames:
Gdx.graphics.getDeltaTime();
So, changing your code accordingly should make your player move smoothly (depends on velocity):
ninjaX = ninjaX + ninjaVelocity * Gdx.graphics.getDeltaTime();
Same thing for substraction in your else block.
You also probably want to set x coordinate (ninjaX) as well as y (currently you're no passing it in your setPosition method):
setPosition(ninjaX, ninjaY);
*I'm guessing here because I don't know what the method does
This is just an example of how you could solve this.
You don't give an explanation of what setPosition() does and why it is called so I will ignore it.
In your render():
public void render(float delta){
....
ninja.update(delta);
}
In your Ninja.class
private final Vector2 leftPos = new Vector(20, 60);
private final Vector2 rightPos = new Vector(80, 60);
private Rectangle bound = new Rectangle(80,60,32,32);
private Vector2 currentPos;
private float moveSpeed = 100f;
public Ninja(){
currentPos = leftPos;
}
public void update(float delta){
if(bound.x > currentPos.x)bound.x += moveSpeed * delta;
else bound.x -= moveSpeed * delta;
}
public void move(){
if(currentPos == leftPos)currentPos = rightPos;
else currentPos = leftPos;
}
Then in you input processor call ninja.move() in touchDown()
None of this is tested.
The reason you need to multiply moveSpeed with delta time is to make movement independent of fps. This way ninja will move the same distance in the same time no matter how many fps the game runs.
Heads up, I'm pretty new to this whole thing, so if I use some incorrect verbage, I apologize.
Do Number data types cause problems when creating classes? I have a class called Asteroid and when it is instantiated, I declare some properties that I assign values to at run time such as .zVelocity, .xVelocity and .yVelocity for example. When they are declared as int data types, everything compiles and runs fine, but I run into some jerky motion when properties are altered through my trigonometry functions. So I changed them all to Number data types and suddenly something very strange happens... the Asteroid instances no longer load to their correct .x and .y coordinates when the .swf is run. Instead, the all load to the origin of the stage object in the top left corner. If I change .xVelocity data type back to int, then all the Sprite objects render along the top of the stage horizontally. I hope this is making sense. I could upload the file, but I'll need a little guidance on that.
package {
import flash.display.Sprite;
public class Asteroid extends Sprite {
var xVelocity: Number;
var yVelocity: int;
//var zVelocity: Number;
var velocity: Number;
var factor: uint;
public function Asteroid(): void {
//xVelocity = Math.floor(Math.random()*6)-3;
//yVelocity = Math.floor(Math.random()*4)-2;
//zVelocity = Math.floor(Math.random()*4);
}
}
}
and my Document Class:
//////
// TIMER
//////
timer.start();
timer.addEventListener(TimerEvent.TIMER, timerFunction);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, timerComplete);
function timerFunction(e: TimerEvent): void {
//trace("length: " + asteroids.length);
if (asteroids.length < 1000) {
newAsteroid();
}
for (var i: int = 0; i <= asteroids.length - 1; i++) {
asteroids[i].alpha -= .05;
asteroids[i].x += asteroids[i].xVelocity;
asteroids[i].y += asteroids[i].yVelocity;
//asteroids[i].z += asteroids[i].zVelocity;
//asteroids[i].zVelocity += .1 ;
//asteroids[i].xVelocity /= 1.1;
//asteroids[i].yVelocity /= 1.1;
if (asteroids[i].alpha <= .05) {
asteroids.splice(i, 1);
removeChild(asteroids[i]);
i--;
}
}
}
function timerComplete(e: TimerEvent): void {
trace("stop");
}
//////
// Asteroids
//////
function newAsteroid(): void {
for (var i: int = 0; i <= 100; i++) {
var idx: int = Math.floor(Math.random() * colorArray.length);
asteroid = new Asteroid();
asteroid.x = (Math.random() * stage.stageWidth * .5 + stage.stageWidth * .25) as Number;
asteroid.y = (Math.random() * (stage.stageHeight - 100) + 50) as Number;
asteroid.graphics.lineStyle();
asteroid.graphics.beginFill(colorArray[idx], .6);
asteroid.graphics.drawRect(0, 0, 5, 5);
addChild(asteroid);
asteroids.push(asteroid);
}
}
}
}
}
I'm probably missing something really simple. Oh, and feel free to offer advice on any awful habits you see me getting into, but to fully correct my code is probably beyond the scope of this single question, although any attempts will be received warmly.
**Edit
The jerky motion was solved with by implementing the answer marked as correct below!
int types have a default value of 0 and that's why they work in your case but the downside is that they do round up all their value to integer which causes weird effect in animation.
Number types have a default value of NaN (not a number) and that is why you are having a lot of trouble.
You never give your variables a default value (you never instantiate them), in the case of int that works since they start at zero but in the case of Number that doesn't work since they start with NaN as default value. You keep trying to use those but they only return NaN all the time and nothing works.
I'm guessing by now you know what to do but in case:
Give your variable a value before using them!
I'm trying to make a basic game in Flash and Actionscript 3.
As of now, I've been working on a smooth game loop but ran into problems. I tried to implement a fixed time-stamp loop seen: http://gafferongames.com/game-physics/fix-your-timestep/ and in Flixel.
The main issue that I have right now is that moving a simple object across the screen produces noticeable stuttering. I am aiming for a smoother experience but can't seem to figure out what the issue is.
The main loop is called on an Event.ENTER_FRAME at 60 fps.
These variables are instantiated first:
public var total:uint = 0;
public var fixedDT:Number = 1000.0/60.0; //Shoot for 60 FPS in MS
public var accumulator:int = 0;
public var maxAccumulation:uint = 120;
This is the main loop on every ENTER_FRAME:
//Most times are in ms
var mark:uint = getTimer();
var elapsedMS:uint = mark-total;
total = mark;
accumulator += elapsedMS;
if(accumulator > maxAccumulation){
accumulator = maxAccumulation;
}
while(accumulator > fixedDT){
step();
accumulator = accumulator - fixedDT;
}
//Convert from ms to secs. to interpolate graphics drawing (linear interpolation)
renderGameState(accumulator/fixedDT/1000.0);
step() is just updating every game-object with the fixed delta-time. The game object update function is simple and is as follows:
//First part is just updating the previous position for graphic interpolation
position.x += velocity.x*deltaTime;
position.y += velocity.y*deltaTime;
For rendering, I am just drawing bitmap.copyPixel. The graphical interpolation I mentioned is using a basic linear interpolation function that uses prev./curr. position and deltaTime to calculate the drawX/Y.
public function render(bitmap:BitmapData, deltaTime:Number, xOff:Number, yOff:Number):void{
this.x = lerp(prevPosition.x,position.x,deltaTime) + xOff;
this.y = lerp(prevPosition.y,position.y,deltaTime) + yOff;
bitmap.copyPixels(bitmapData, bitmapData.rect,new Point(this.x,this.y),null,null,true);
}
public function lerp(v0:Number, v1:Number, t:Number):Number {
return (1-t)*v0 + t*v1;
}
However, there is noticeable stuttering appearing. In the image below, I don't clear the bitmap before drawing to it. You should be able to see that there's a lot of variation between the spacing of circles rendered, and sometimes it's extremely noticeable.
http://i.stack.imgur.com/00c39.png
I would appreciate any help at all, thanks!
I don't know if this helps but here's the code I use to fix my time step.
private var _pause :Boolean;
private var _prevTimeMS :int;
private var _simulationTime :Number;
override public function update():void
{
super.update();
if (!_pause)
{
var curTimeMS:uint = getTimer();
if (curTimeMS == _prevTimeMS)
{
return;
}
var deltaTime:Number = (curTimeMS - _prevTimeMS) / 1000;
if (deltaTime > 0.05)
{
deltaTime = 0.05;
}
_prevTimeMS = curTimeMS;
_simulationTime += deltaTime;
while (space.elapsedTime < _simulationTime)
{
// Your game step goes here.
_space.step((stage.frameRate > 0) ? (1 / stage.frameRate) : (1 / 60));
}
}
}
(Originally taken from a Nape Physics sample)
Alright, so i've got some code that says that if a hit test is preformed, a variable is increased by 1. However, when I run it, I get crazy numbers like 1,1,1,2,5,3,2,5,2,3,4,1,1
There's no pattern, it's just random. Here's the code:
public function loop(e:Event)
{
y += speed;
if (y > stage.stageHeight)
{
setupAsteroid(false);
}
//hittest for the asteroid
if (hitTestObject(target))
{
stageRef.addChild(new Explosion(stageRef, x, y));
trace("Hit");
kills ++;
trace(kills);
if(kills == 3){
trace("Success");
}
}
I've included all the code in the function used, and not all of it is necessary to the hit test as you can see. also, the variable is declared correctly as
private var kills:Number = 0;
inside the same .as file and class.
You add an Explosion to the stage but there is no sign of you removing anything from the stage, or at least stopping the check for hitTest. Also I'm guessing that your function loop(e:Event) listens for an ENTER_FRAME event and in that case if you're not removing anything from the stage that hitTest is going to be returning true every frame and creating a new Explosion every frame. Why not just add a public variable to target, like public var destroyed:boolean = false; and reconstruct your hitTest to read if(hitTestObject(target) && !target.destroyed) and in the body of your conditional just add target.destroyed = true;
More code would definitely help though.
I have implemented a gameloop in Flash/Actionscript/Starling and I want to throw it at you to see if this is a valid implementation.
I wanted to have a variable time step approach.
private var _deltaTime:Number = 0;
private var _lastTime:Number = 0;
private var _speed = 1000 / 40;
private function onEnterFrame() {
var now = new Date().getTime();
var delta = now - _lastTime;
_deltaTime += delta - _speed;
_lastTime = now;
//skip if frame rate to fast
if (_deltaTime <= -_speed) {
_deltaTime += _speed;
return;
}
update();
}
private function update() {
updateGameState();
if (_deltaTime >= _speed) {
_deltaTime -= _speed;
update();
}
}
What I got sofar is that I have a constant speed (more or less).
My question is is there a better approach so that the movements will appear even
smoother.
What is really surprising to me is that even thou the FPS is pretty much constant (60FPS)
the movement is sometimes bumpy yet smoother than with the naive gameloop.
Youre on the right track - assuming that onEnterFrame is triggered in some way by Event.ENTER_FRAME - instead of skipping update, call it on every frame but pass in the time elapsed:
private function onEnterFrame() {
var now = new Date().getTime();
var delta = now - _lastTime;
_lastTime = now;
updateGameState(delta/1000);//divide by 1000 to give time in seconds
}
In updateGameState, you can utilise 'delta' to calculate movement etc, eg:
function updateGameState(timeElapsed:Number):void {
myAlien.x += myAlienSpeedPerSecond*timeElapsed;
}
This way you get smooth movement even when frame rate varies.
from the Starling introduction pages, it shows that time elapsed is built into the EnterFrameEvent class.
// the corresponding event listener
private function onEnterFrame(event:EnterFrameEvent):void
{
trace("Time passed since last frame: " + event.passedTime);
enemy.moveBy(event.passedTime * enemy.velocity);
}
http://wiki.starling-framework.org/manual/animation#enterframeevent