TList cycle through objects, not linear.
BlitzMax Forums/BlitzMax Beginners Area/TList cycle through objects, not linear.
| ||
I'm making a space invaders game. I have the enemies running from right to left and back. I had to make a Global direction variable and integrate that in the movement of the enemy-objects to make it work because when i tried to change all individual direction variables some were left untouched and some were changed twice. So the global is a solution for this problem. But now i have the problem of the y movement. I want to move all enemies 20 points down on the y-axle when a (whichever) enemy changes direction on the x-axle. I really don't want to make the y a global because it limits my options with the object. I tried to do a single change of y for all enemy-objects with EachIn in the Update of the object itself but this means i do a Eachin on a Eachin for the same object so i tried to limited the calls to the number of enemies but it doesn't do what i expected: some objects move down 20 points, some move down 40 points and some dont move down. I keep all objects in 1 list and call them by type, this works (to my suprise) but is this whats causing the inconsistent object-updates? I really want to keep all objects in the same list for the same reason that some people need to drink coffee after dinner or wash their hands every 30 minutes :) Sorry for the lack of code but i'm at work and don't have the code here. I really hope to solve this tonight at home. Anyone any suggestions on how to do a change on all objects of a certain type (in a big list of several types but all extends within same object-tree) as soon as a trigger (one of the objects hits the x margin) is set off? *Will post the code tonight. |
| ||
Sounds like you have added the objects to the list more than once or in some cases not at all... I use lists all the time, lists for aliens, lists for bullets, lists for particles and they work fine so you must have done something crazy :-) |
| ||
No actually, each object has been added once to my GameObjectList. The regular code works fine, all enemies move from left to right and back again (just like space invaders). This is done with a simple For EachIn cycle with a Update and Draw method. All fine so far. Now when one of the enemies hits the x-margin, the direction (a global variable 1/-1) is changed, this works fine. But i want it to also move all enemies down (y) 20 points when the x-margin is hit. The problem is that the trigger happens with the For EachIn loop and i want to update the y of all enemies at that moment. Just a simple : For Local e:TEnemy EachIn GameObjectList e.y :+ 20 Next The hard part is to have it do this once for every object. At this moment it will move all my objects down of the screen. So i cant get it to stop doing +20 or when i restrict the change it leaves the first object (the one that set off the trigger) at the original y position or it moves random objects on y and leaves others alone. |
| ||
OK Oh, this is nice and easy. Before your main For loop (the one that moves them all sideways) make a Local variable called MoveDown=0. Then when the x margin is hit, set MoveDown=1 and DON'T loop through the ObjectList yet. After the main loop is complete you can check if MoveDown=1 and if so run the loop which changes the y coord, thus it only happens once! :-) |
| ||
I think i see where you're going but that would be if the movement of the object is in my main gameloop. I placed the movement in my Update Method of the object. My main loop is this: While 'NOT ESC' cls If Enemies=0; CreateWave() //Grey Alien solution If MoveDown= 1 For Local e:TEnemy EachIn GameObjectList e.y :+ 20 Next MoveDown = 0 End if //----------------------- For Local o:TGameObject EachIn GameObjectList o.Update o.Draw Next Flip CalculateDelta() Wend I think i tried something like this and it didnt work (though it should). My problem is probably that i want all my gamelogic inside my Type methods, i'll try this tonight at home. Thanks Grey. |
| ||
OK make MoveDown a GLOBAL instead. Then you can set it inside your Type methods and process it outside, after your For Local o:TGameObject EachIn GameObjectList loop. |
| ||
Yeah, indeed Global. In my code the y is updated next frame and then drawn. If i were to use your way i would have to split Update & Draw and i would have an additional For EachIn loop. This offcourse if with your way you want to update the y the same frame, if this is not the case then i don't see the difference between your way and my way. |
| ||
Oh yeah you 100% should separate the logic and draw and later on you should get some timing code in there so that your logic may occur several times before it is drawn... |
| ||
Okay, the CalculateDelta updates a AlphaTime and i multiply all changes on the x and y by AlphaTime. y :+ (20 * AlphaTime) I kept it out of the posts to keep it readable. So i need more timingcode than that? Just out of curiousity, can you name a situation where i would need to have my logic occur multiple times before i draw? Am i on the right track with putting all the logic in Type mothods or should i keep the Types simple and slim and have all the gamelogic in the mainloop (or function(s) which i call from the mainloop)? Or is all of this just a personal design preference? Thanks for all your help so far. |
| ||
can you name a situation where i would need to have my logic occur multiple times before i draw? Yeah if you used fixed rate logic. It won't occur if you are using delta time. Even so you should always process your logic first, and then do the drawing later completely separately (not part of same loop). I agree that keeping the logic in the game types is correct. It's just you if you process all the logic first then you can set global flags like I mentioned to change everything before it's drawn. |
| ||
Grey or anyone else, please help me out. I have the Y step working but slowly the enemy object are moving out of alignment. I increased the speed-multiplier so you can see what happens very quickly. I dont have an uploadplace (never needed one the last 10 years) but i dont use any real art yet anyway, just 2 random pictures until i have the basic framework up and running. So just select 2 random (small) pictures to use with the game. What am i doing wrong that results in this de-alignment, shouldn't my DeltaTiming prevent this from happening? 'Space Invaders v0.1 'Philip 'Start: 27 aug 2008 SuperStrict 'Declare files Incbin "ship.png" Incbin "missile.png" 'Declare constants Global ScreenWidth: Int = 1024 Global ScreenHeight: Int = 768 Global ScreenColorDepth: Int = 32 'Screen resolution Graphics ScreenWidth, ScreenHeight, ScreenColorDepth 'Hide the Mousepointer HideMouse() 'Declare types 'Default for all Gameobjects Type TGameObject Field x:Float, y:Float Field speed:Float Field life: Int Field img:TImage Field xmargin:Int, ymargin: Int Method Draw() DrawImage(img, X, Y) End Method Method Update() Abstract End Type 'Thats you, the player Type TPlayer Extends TGameObject Field FireDelay: Int Field TempDelay: Int = 0 Field Weapon: Int = 0 Field WeaponSpeed: Int = 6 Function Create:TPlayer(px:Int, py:Int, pspeed:Int, plife:Int, pdelay:Int, pimg:String) Local P:TPlayer = New TPlayer P.x = px P.y = py P.speed = pspeed P.life = plife P.FireDelay = pdelay P.img = LoadImage(pimg) P.xmargin = ImageWidth(P.img) / 2 P.ymargin = ImageHeight(P.img) / 2 MidHandleImage(P.img) ListAddLast GameObjectList, P Return P End Function Method Update() If KeyHit(KEY_SPACE) Player.Shoot() EndIf If KeyHit(KEY_UP) Player.SwapWeapon() EndIf If KeyDown(KEY_LEFT) Player.x :- (Player.speed * AlphaTime) EndIf If KeyDown(KEY_RIGHT) Player.x :+ (Player.speed * AlphaTime) EndIf If x < xmargin ; x = xmargin If x > ScreenWidth - xmargin ; x = ScreenWidth - xmargin TempDelay:- 1 End Method Method SwapWeapon() Weapon:+ 1 If Weapon = 2; Weapon = 0 End Method Method Shoot() If TempDelay < 0 Select Weapon Case 0 WeaponSpeed = 4 FireDelay = 15 Case 1 WeaponSpeed = 2 FireDelay = 8 End Select Local S:TBullet = TBullet.Create(x , y - ymargin , - 1 , WeaponSpeed , 1 , "incbin::missile.png") TempDelay = FireDelay EndIf End Method End Type 'The default for all Enemies Type TEnemy Extends TGameObject Field StepYLocal:Int =0 Function Create:TEnemy(ex:Int, ey:Int, espeed:Float, elife:Int, estepylocal:Int, eimg:String) Local E:TEnemy = New TEnemy E.x = ex E.y = ey E.speed = espeed E.life = elife E.StepYLocal = estepylocal E.img = LoadImage(eimg) E.xmargin = ImageWidth(e.img) / 2 E.ymargin = ImageHeight(e.img) / 2 MidHandleImage(E.img) ListAddLast GameObjectList, E Return E End Function Method Update() x :+ ( (EnemyDir * speed) * AlphaTime) If x < xmargin Or x > (ScreenWidth - xmargin) EnemyDir :* - 1 StepY = 1 EndIf End Method Method UpdateY() y :+ (StepYLocal * AlphaTime) speed:+ ((speed/10) * AlphaTime) End Method End Type 'The default Bullet Type TBullet Extends TGameObject Field direction: Int = 0 Function Create:TBullet(bx:Int, by:Int, bdirection:Int, bspeed:Int, blife:Int, bimg:String) Local B:TBullet = New TBullet B.x = bx B.y = by B.direction = bdirection B.speed = bspeed B.life = blife B.img = LoadImage(bimg) B.ymargin = ImageHeight(B.img) / 2 B.y :- B.ymargin MidHandleImage(B.img) ListAddLast GameObjectList, B Return B End Function Method Update() y :+ ( (direction * speed) * AlphaTime) If Life <= 0 Or x < 0 Or x > ScreenWidth Or y < 0 Or y > ScreenHeight GameObjectList.Remove(Self) End If End Method End Type 'Declare placeholders and globals Global GameObjectList:TList = CreateList() Global Player:TPlayer = TPlayer.Create(ScreenWidth/2, ScreenHeight-30, 4, 3, 3, "incbin::ship.png") Global LastTime:Int = MilliSecs() Global AlphaTime:Double Global Enemies:Int = 0 Global EnemyDir: Int = 1 Global StepY:Int = 0 'Main loop While Not KeyDown(Key_Escape) Local o:TGameObject Local e:TEnemy Cls If Enemies = 0; Wave() For o = EachIn GameObjectList o.Update() Next 'Possible Y update If StepY > 0 For e = EachIn GameObjectList e.UpdateY() Next StepY = 0 EndIf 'Draw actual For o = EachIn GameObjectList o.Draw() Next Flip DeltaCalculate() Wend GCCollect() End Function DeltaCalculate() Local CurTime:Int = MilliSecs() AlphaTime = (CurTime - LastTime) / 10.0 LastTime = CurTime End Function Function Wave() EnemyDir = 1 For Local EnemyRow:Int = 1 To 8 For Local EnemyColumn:Int = 1 To 5 Local Enemy:TEnemy = TEnemy.Create(150 + (50 * EnemyRow) , 10 + (50 * EnemyColumn) , 0.1, 20, 1, "Incbin::ship.png") GameObjectList.Addlast(Enemy) Enemies:+ 1 Next Next End Function |
| ||
Problem solved! Okay, sorry. You stare at your code for 3 hours straight to find an error. Then right after you give up and ask for an answer on the forum the solution hits you right in the face. Because i have 1 object triggering the directional Global all objects after that one are moved with the new direction except offcource the object that triggered the directional change because it has already been updated. I just had to move the: EnemyDir :* - 1 |
| ||
Just as a general idea and not say something bad about your code: If all "enemies" change their y-coordinate simultanous there is no need to change each y-field. Just have a global moveY-field within the TEnemy-Type and change it when needed. When drawing/updating/calculating you easily add moveY to the y-field [ drawimage(self.x,self.y+self.moveY) ]. Although this seems unnecessary for a list with 20 objects, but imagine something like "sidecrolling effects",instead of moving each tile of a level you just "displace" the view with changing only one variable. bye MichaelB |