Slick Forums

Discuss the Slick 2D Library
It is currently Wed Jun 19, 2013 10:21 am

All times are UTC




Post new topic Reply to topic  [ 20 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Sun Jul 08, 2012 7:34 pm 
Offline

Joined: Sun Jul 08, 2012 10:33 am
Posts: 27
Hey guys,
first of all im german so it may be that my english isnt that good and i express my problem a bit complicate :)

so i got many questions and i think they all belong to this subforum.
now first what i got. i am coding a tower defence game with Slick2D (BasedStateGame). i already got a map editor, some towers, units walking a given path and the towers shoot them down.

i wont post my code right know because i think my question can be answered without it.
so here are my questions/problems for which i couldnt find a answer here yet:
a)
i have a 'Play' class which manages the whole game process.
to animate the firing speed of a tower or the movementspeed of a unit is use a 'counter' attribute which increments by 1 everytime the 'update' method is called.
Question 1: is this a good way to implement this or are there better ways to do this (maybe even smth already in the lib?)
i ask this because if there is no other way to do it. there will be a limited speed for the movement of the units which istn really fast because my max fps is 100. I would have to increase them to 200 for doubled speed then (but i actually dont want to do it this way).
Question 2: the shot of my towers are drawn everytime a 'isShooting' attribute of my tower class is set. but sometimes the shot just isnt drawn but the tower still deals dmg (just the drawing of the shot is missing). hopefully someone got this problem before and can give me a hint :)


b)
the overridden 'init()' method of a state is always executed 2 times. Is this on purpose or did i smth wrong? to avoid this i used an boolean attribute 'firstTimeInit' because else i had always 2 inputlisteners and everytime i pressed a key it was recorded 2 times.

c)
to keep track of all the towers and units on the map im in a dilemma. i tried both ArrayLists and arrays but i really dont know what i should use or if these are even good choices. Both have advantages and disadvantages i experienced while coding but i cant decide which to use. I would appreciate it if someone could give me some ideas when to use which or give me an even better structure to store them.

i didnt check others games by now cause i dont have much free time, but if u know a game of the same style which a good way of implementation let me know it (a link would be nice)

greets Khorne


Last edited by Khorne on Sat Jul 21, 2012 4:49 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Sun Jul 08, 2012 8:31 pm 
Offline
Slick Zombie

Joined: Sat Jan 27, 2007 7:10 pm
Posts: 1477
a) No, this is not a good way to implement this. Counters should be based on the delta time; this value is the amount of milliseconds that has passed since the last update. The reason we don't use fixed values for time based animations (like incrementing by 1 every update) is because update is not guaranteed to be called in a fixed fashion. i.e. A fast machine might call update 1000 times per second, whereas a slow machine might only call it 60 times per second.

So, instead, we move units like this:
Code:
player.x += delta * MOVE_SPEED;


And update a timer like this:
Code:
public static final int DELAY = 1000; // 1 second

... in update...
timeElapsed += delta;
if (timeElapsed >= DELAY) {
    timeElapsed = 0;
    trigger();
}


Regarding question two, it sounds like a bug in your code.

b) You probably didn't set up your states properly. There was a tutorial on the wiki that showed incorrect usage; I fixed it not too long ago. You can refer to the latest tests for proper usage of state based games and other Slick features:
https://bitbucket.org/kevglass/slick/sr ... dTest.java

c) ArrayLists are what you need.

Virium is a good example of a top-down game using many entities on screen. There are other examples in the trunk that may help.


Top
 Profile  
 
PostPosted: Mon Jul 09, 2012 12:50 pm 
Offline

Joined: Sun Jul 08, 2012 10:33 am
Posts: 27
hi davedes,
thanks for your answer.
the problem with the init() is solved and i changed everything back to ArrayLists instead of arrays.

now i understand the idea of the delta value but i dont get it how to use it exactly; i searched for a tutorial on delta usage but couldnt find anything :_(.
so my problem here are, again, the towers. they should shoot with different speed values (in this case i also used milliseconds). so for a tower which should shoot every 1second the 'speed' attribute is set to 1000. but i cant figure out how to combine this with the 'delta' and/or the 'timeElapsed' attribute. in fact i dont get what 'timeElapsed' is good for?!

following method is called in my 'Play' class (play state).
Code:
private void towersVsNpcs(int timeElapsed) {
   for(SpriteTower towerSprite: towersOnMap) {
      if (towerSprite.getTower().speed <= timeElapsed) {
         attack(towerSprite, npcs);
      }
   }
}


so in combination with your timerupdate of your last post it will never be executed oO

edit: explanation of my code
for each tower which was build on the map the method checks the 'speed' of the tower (this is the problem). if the time is passed the tower attacks (the 'attack()' method is executed). in the 'attack()' checks for each npc if its in range of the tower. if it is the npc will be added to a 'targets' list of the specific tower.

Another question now is: how to synch the animation of my shot and the dmgdealing of the tower. i used before a attribute 'isShooting' for each tower which was set everytime a npc was added to the 'targets' list of the tower. i think this was why the animation of the shot wasnt drawn everytime. now i just said:
update: if the tower is ready to shoot and if a npc is in range of the tower: calculate the dmg the npc gets and add it to its target list.
render: if the tower's target list is not empty, draw the shooting.
but as said they are not synchronized...

i hope u can understand my problems, if u need more code of my SpriteTower class or smth else let me know.
thanks


Top
 Profile  
 
PostPosted: Tue Jul 10, 2012 8:00 am 
Offline
User avatar

Joined: Wed Jun 20, 2012 8:10 pm
Posts: 54
You're doing it wrong :D

No seriously, the example from davedes was pretty clear.
The update fonction is called at an interval which is not always the same, so for example is the first call is at 0ms, the second can be at 50ms, the third at 125ms, etc etc...
So each time you call the update the delta variable is here to give you the time in ms since the last execution, so you need to use that.

So what you need to do is store the time somewhere for example with your fonction and using davedes example, in your update method you need to do this :

Code:
MyClass extends BasedGameState{

int timeElapsed = 0;

public void update (..., int delta) {
timeElapsed += delta;
   for(SpriteTower towerSprite: towersOnMap) {
      if (towerSprite.getTower().speed <= timeElapsed) {
         attack(towerSprite, npcs);
      }
   }
}

}


There you have it, you can put this code into a separate class which can be better but you would need to have the same mechanism and pass the delta variable as a parameter, it's the only way to have the correct timing.

Same way, to synchronize, you need to put your treatment in the update, and put some kind of boolean for example which will set the animation, like :

Code:
MyClass extends BasedGameState{

   boolean isShooting;

   public void update(...){

      if (conditiontoshoot.isMet()){
         ... my treatment ...
         isShooting = true;
      }

      if (myShot.isOver(){
         isShooting = false;
      }

   }

   public void render(...){

      if (isShooting){
         myAnimation.render(...)
      }
   }
}


Of course, again, you can have a more complex design, but ultimately, you still need to use the update/render method to synchronize everything.

_________________
Seventeen


Top
 Profile  
 
PostPosted: Sun Jul 15, 2012 7:00 am 
Offline

Joined: Sun Jul 08, 2012 10:33 am
Posts: 27
hi seventeen,

in my opinion this just won't work. lets assume that the update function si called every 50ms so 'delta' will always be 50. Now we got 2 different towers with different attack speed (first every 250ms and second every 2000ms). Using davedes update example 'timeElapsed' will be resetted every time its >=1000.
the first tower will then shoot every update where '250<=timeElapsed' which is 15times in a row right after the timer is greater than 250 (750ms). For the second tower, it will never shoot because timeElapsed will never be greater then 1000.
So then i thought actually 'speed>=timeElapsed' should be the statement but even there are the same problems just twisted. the second tower will always shoot and the first shoots 5times in a row and then waits 750ms until the timer is resetted

So if i'm not wrong shouldn't i need a 'timeElapsed' attribute for every tower? then i can write:

Code:
for(Tower tower: towersOnMap) {
      tower.updateTime(delta);
      if (tower.getTower().speed <= tower.getTimeElapsed()) {
         attack(tower, npcs);
         tower.resetTime();
      }
}


and the 'updateTime()' , 'resetTime()' methods in class 'Tower' would look like this:

Code:
public void updateTime(int delta) {
      timeElapsed += delta;
}

public void resetTime() {
      timeElapsed -= speed;
}

sry if i got it wrong but i really cant see how this should work :cry:

edit: if u wonder why i didnt reset the time to 0 but substracted just the speed: i think this should be better for the exactness of time and also if delta should be somehow really great it may happen that its greater than a multiple of the speed of a tower so the tower would have shot more than once in this case (but i guess this wont happen (often)).


Top
 Profile  
 
PostPosted: Tue Jul 24, 2012 1:17 pm 
Offline

Joined: Sun Jul 08, 2012 10:33 am
Posts: 27
would be nice if i could get an answer soon :) then i can go on and debug the problem a2) from my first post. ill post the code i have atm with my counter (which i change as soon the previous q is solved):
Problem: the shot of my towers are drawn everytime a 'isReadyToShoot' attribute of my tower class is set and the targetList of the tower is not empty. but sometimes the shot just isnt drawn but the tower still deals dmg (just the drawing of the shot is missing)

Code:
public void render(GameContainer gc, StateBasedGame sbg, Graphics g) {
    for(Tower tower: towersOnMap) {
        tower.drawShot(g);
    }
}

Code:
public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException {
    for(Tower tower: towersOnMap) {
        if (tower.isReadyToShoot()) {
            attack(tower, npcs);
        }
        tower.updateShootCounter();
    }
}

public void attack(Tower tower, ArrayList<SpriteNpc> npcs) {
        switch(tower.getType()) {
        case Tower.SINGLE: singleAtk(tower, npcs); break;
        default: break;
        }
}

private void singleAtk(Tower tower, ArrayList<SpriteNpc> npcs) {
    for(int j=0; j<npcs.size(); j++) {
        SpriteNpc npc = npcs.get(j);            
        if (npc.isSpawned()) {
            if (tower.getRadiusOfTower().intersects(npc.getCollisionShape())) {
                tower.addTarget(npc);
                npc.hp = npc.hp - tower.dmg;
                if (npc.hp <= 0) {
                    Singleton.getInstance().getPlayer().increaseCash(npc.getIncome());
                    npcs.remove(j);
                }               
                break;
            }
        }
    }
}

Code:
public class Tower extends SpriteBuilding{
    //...
    public void drawShot(Graphics g) {
        if (targets.size() != 0 && isReadyToShoot) {
            g.setColor(Color.white);
            for(int i=0; i<targets.size(); i++) {            
                g.drawLine( radiusOfTower.getCenterX(), radiusOfTower.getCenterY(),
                                targets.get(i).getX()+imgW/2, targets.get(i).getY()+imgH/2);
            }
            targets.clear();
            setReadyToShoot(false);
        }
    }

    public void addTarget(SpriteNpc npc) {
        targets.add(npc);
    }

    public void updateShootCounter() {      
        if (!readyToShoot) {
            shootCounter = (shootCounter+1) % speed;
            if (shootCounter == 0) {
                readyToShoot = true;
            }
        }
    }
}


maybe someone can find the problem why the shot isnt drawn correct with this.

edit: some explanations
i saved all npcs which will spawn in a wave in a ArrayList and every time a npc spawn its 'isSpawned' attribute is set to true. the towers check only for those npcs which are already spawned.


Top
 Profile  
 
PostPosted: Tue Jul 24, 2012 1:42 pm 
Offline
User avatar

Joined: Wed Jun 20, 2012 8:10 pm
Posts: 54
Concerning the timers, not to be rude, but this has nothing to do with Slick but basic programming.
You can indeed have one variable for each tower and check the timeElapsed for both.

Concerning the drawing problem, you're doing :

Code:
tower.drawShot(g);


and in this method you have :

Code:
if (targets.size() != 0 && isReadyToShoot) {
            .... draw something ....
            targets.clear();
        }


Again, there's something really wrong with your code. The render method is not here to do any kind of treatment but to "render" something, so this : targets.clear(); is not good, and the way i'm reading your code, probably where the problem lies.
Since the method render is called every x ms (x being pretty low), it will do this :

render first call :
targets size is 6 : i draw 6 line
i clear the targets

render second call : (like a time way to low for the naked eye to actually see the drawing happen in the first call)
targets size is 0 : i draw ... Well nothing

...

render gazillion call :
targets size is 0 : still nothing to draw here !!! What do?? :D

Get it?

You should consider tutorials on the way update & render couple actually works.
The way it sould be done is something like :
Code:
update(...){
   if (i shoot)
      shooting = true;
   else
   shooting = false;
}

render(...){
   if (shooting){
      i draw my shoot
   }
}


I'm seeing this is exactly what i've already been saying in this post... :|

_________________
Seventeen


Top
 Profile  
 
PostPosted: Tue Jul 24, 2012 3:09 pm 
Offline

Joined: Sun Jul 08, 2012 10:33 am
Posts: 27
well now i see what the problem is. i thought fps would contain render and update together... so if i have 100fps its render and update 100 times per second but it doesnt mean that update is called sequentially right after render?

Quote:
I'm seeing this is exactly what i've already been saying in this post...


well actually these are 2 different things i meantioned. but you indirectly solved the previous problem too :) thanks
i thought i had to solve it with just one global 'timeElapsed' attribute which i couldnt manage but now its fine.

Quote:
You should consider tutorials on the way update & render couple actually works.

i knew that render is just for rendering but as mentioned at the top i had some thinking mistakes. ill fix that and thanks again.

so ill be back soon i guess ;)


Top
 Profile  
 
PostPosted: Wed Jul 25, 2012 8:49 am 
Offline
User avatar

Joined: Wed Jun 20, 2012 8:10 pm
Posts: 54
Then again if we don't make mistakes and are always satisfied with our work we can't progress.

I think the execution of render and update are not sequential, even if they are (which i doubt) i think you should consider your program working like that, it'll avoid future issues.

Good luck with your game. :wink:

_________________
Seventeen


Top
 Profile  
 
PostPosted: Wed Jul 25, 2012 1:39 pm 
Offline
Regular
User avatar

Joined: Thu May 05, 2011 8:35 pm
Posts: 231
Location: Somewhere between the bits and bytes
The render and update are indeed sequential, the gameloop works about like this.

Code:
int targetFps = 60;

while(running){
   update(getDelta());
   render(g);
   sync(targetFps);
}

int getDelta(){
   // returns the amount of time in milliseconds passed since last call.
}

void sync(int targetFps ){
   // makes sure that at least 1000/targetFps milliseconds has passed since last call, by making the thread sleep, if needed.
}

_________________
For every new problem, a new source of solutions has come to exist.


Top
 Profile  
 
PostPosted: Mon Jul 30, 2012 9:07 am 
Offline

Joined: Sun Jul 08, 2012 10:33 am
Posts: 27
hey Magn919,
ok now then i'm a little bit confused cause of a bug i have which (i think) shouldn't occure if they were sequential.
it's still the render problem of my tower. i worked over my code by now but the problem is still there. so heres the updated version:

Code:
update:
private void towersVsNpcs(int delta) {
   for(Tower tower: towersOnMap) {
      tower.updateTime(delta);
      if (tower.getTimeElapsed() >= tower.speed) {
         tower.setReadyToShoot(true);
         tower.resetTime();
         tower.dealDmg();
      } else {
         tower.setReadyToShoot(false);
      }
   }
}


Code:
render:
private void drawShooting(Graphics g) {
   for(Tower tower: towersOnMap) {
      tower.drawShooting(g);
   }
}


Code:
Tower Class:
public void drawShooting(Graphics g) {
   if (readyToShoot && mainTarget != null) {
      g.setColor(Color.white);         
      g.drawLine( radiusOfTower.getCenterX(), radiusOfTower.getCenterY(),
               mainTarget.getX()+imgW/2, mainTarget.getY()+imgH/2);
   }
}

public void resetTime() {
   timeElapsed -= speed;
}


so the npcs still get dmg as they should but sometimes the drawing of the shot is missing.

edit: sry accidently submitted instead of preview :) i wasn't finished yet with my post. so if you already began to read im sry ...


Top
 Profile  
 
PostPosted: Mon Jul 30, 2012 12:32 pm 
Offline
User avatar

Joined: Wed Jun 20, 2012 8:10 pm
Posts: 54
So basically you did not read like at all my post...? :shock:

The bug i mentioned being the one causing the trouble of the rendering is still there!!!!
You seem to have coded differently exactly the same behaviour : the update is called, cleans the boolean setting the shooting, since the update is called several times per second, you won't see the shot being made!!!

Please reread thoroughly the answer put in this topic.

_________________
Seventeen


Top
 Profile  
 
PostPosted: Tue Jul 31, 2012 9:01 pm 
Offline

Joined: Sun Jul 08, 2012 10:33 am
Posts: 27
well then the only difference between yours and mine is the "if (i shoot) {...}" well then what do you mean by "i shoot"? is it a attribute of the tower class and if yes when should i set it to true and when to false? the only moment i can set it to true is when starting to draw the shot and right after i should set it back to false (but then there wouldnt be any use because this happens in the same method and update/render are sequential). And if you mean with "i shoot" : enough time has passed since last time (so how often the tower can shoot) then there is no difference (at least i see no difference) between your post and my implementation ...

maybe you can also give me a link to a tutorial plz?

also if the post of Magn919 is true then render is always called right after update:
so every time i set readyToShoot = true in update, it will also be true in render because there is no other method which changes the readyToShoot value in between oO

Seventeen wrote:
Code:
update(...){
   if (i shoot)
      shooting = true;
   else
   shooting = false;
}

render(...){
   if (shooting){
      i draw my shoot
   }
}



Khorne wrote:
Code:
update:
...
   for(Tower tower: towersOnMap) {
      ...
      if (tower.getTimeElapsed() >= tower.speed) {
         tower.setReadyToShoot(true);
         ...
      } else {
         tower.setReadyToShoot(false);
      }
   }
..
}

render:
...
   for(Tower tower: towersOnMap) {
      tower.drawShooting(g);
   }
...

Tower Class:
public void drawShooting(Graphics g) {
   if (readyToShoot && ..) {
      ...
   }
}



edit: just to avoid misunderstandings (if this has smth tot do with it), my shot doesnt have a period/duration. it's just a line which shall be drawn once the tower is ready to shoot.


Top
 Profile  
 
PostPosted: Wed Aug 01, 2012 7:17 am 
Offline
Regular
User avatar

Joined: Thu May 05, 2011 8:35 pm
Posts: 231
Location: Somewhere between the bits and bytes
As i see it, every loop you set readytoshoot to false, except the frame where the timer resets.
Therefore it wont render except that single frame, and will occasionally flicker by, but otherwise wont render anything.

But if you only are shooting a beam, you only need check if there is a target, if there are draw the beam if not dont, but you should keep the timer for correct damage calculation.

_________________
For every new problem, a new source of solutions has come to exist.


Top
 Profile  
 
PostPosted: Wed Aug 01, 2012 9:03 am 
Offline

Joined: Sun Jul 08, 2012 10:33 am
Posts: 27
Magn919 wrote:
As i see it, every loop you set readytoshoot to false, except the frame where the timer resets.
Therefore it wont render except that single frame, and will occasionally flicker by, but otherwise wont render anything.

But if you only are shooting a beam, you only need check if there is a target, if there are draw the beam if not dont, but you should keep the timer for correct damage calculation.



yeah thats how i want it (only drawn in the frame when the tower IS ready to shoot), but now even though the dealDmg() and setReadyToShoot(true) methods are called in the same if statement, the tower deals the expected dmg but the shot is sometimes NOT drawn :(

Code:
for(Tower tower: towersOnMap) {
      tower.updateTime(delta);
      if (tower.getTimeElapsed() >= tower.speed) {
         tower.setReadyToShoot(true);
         tower.resetTime();
         tower.dealDmg();
      } else {
         tower.setReadyToShoot(false);
      }
   }


edit: to say it again xD: i want that a CONTINOUS line from the tower to the npc is drawn if and only if the tower is ready to shoot and the mainTarget != null which is only one frame. no animation, no images, ... are included


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 20 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: Google [Bot] and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group