Question About Lists
BlitzMax Forums/BlitzMax Programming/Question About Lists
| ||
I have one strange issue with list and I would like to see if I am doing something wrong here? I have one list with name Fired_Spell_Explosion_List. Global Fired_Spell_Explosion_List:TList = CreateList() I add some a type object to the list. The type object: Type My_Fired_Spell_Explosion Field Level:Byte Field X:Byte Field Y:Byte Field Explosion_Frame:Byte 'Animation of the spell explosion EndType Global Fired_Spell_Explosion:My_Fired_Spell_Explosion = New My_Fired_Spell_Explosion Add this 2 objects in Fired_Spell_Explosion_List Fired_Spell_Explosion.X = 10 Fired_Spell_Explosion.Y = 5 Fired_Spell_Explosion.Level = 1 Fired_Spell_Explosion.Explosion_Frame = 1 ListAddLast(Fired_Spell_Explosion_List, Fired_Spell_Explosion) Fired_Spell_Explosion.X = 2 Fired_Spell_Explosion.Y = 2 Fired_Spell_Explosion.Level = 1 Fired_Spell_Explosion.Explosion_Frame = 5 ListAddLast(Fired_Spell_Explosion_List, Fired_Spell_Explosion) Then I have one timer and after 1 second I add one Explosion_Frame+1 to Fired_Spell_Explosion Fired_Spell_Explosion.Explosion_Frame:+1 When the variable Fired_Spell_Explosion.Explosion_Frame have the value of 25 I remove 1 object from the Fired_Spell_Explosion_List. For Fired_Spell_Explosion = EachIn Fired_Spell_Explosion_List If Fired_Spell_Explosion.Explosion_Frame >= 25 Then Fired_Spell_Explosion.Explosion_Frame=1 ListRemove(Fired_Spell_Explosion_List, Fired_Spell_Explosion) End If Next There are 2 problems. The objects I added to the list above take the last object values to both objects. (In fact I can't see those values in the list) There is no way to control or select the index of the list. Fired_Spell_Explosion.X = 2 Fired_Spell_Explosion.Y = 2 Fired_Spell_Explosion.Level = 1 Fired_Spell_Explosion.Explosion_Frame = 5 And when the variable Fired_Spell_Explosion.Explosion_Frame >= 25 all objects clear. The timer runs continue and add Fired_Spell_Explosion.Explosion_Frame:+1 If the Explosion_Frame become 25 then I change the value to 1 and remove 1 object. Why in both of the objects in the list have the last values when I add another one abject? And why I don't see both of the objects at once , but then one finished , I see the next. Suppose the for each scans all objects one by one, and all values should be different for each object. When I will try to change the Fired_Spell_Explosion.X and Fired_Spell_Explosion.Y values for each object change the last only. Where is my wrong? This is my example code: We assume all variables was declared. The type and the list are above in this post. The code which adds 2 objects in the list Fired_Spell_Explosion.X = 2 Fired_Spell_Explosion.Y = 14 Fired_Spell_Explosion.Level = 1 Fired_Spell_Explosion.Spell_number = 1 Fired_Spell_Explosion.Explosion_Frame = 5 ListAddLast(Fired_Spell_Explosion_List, Fired_Spell_Explosion) Fired_Spell_Explosion.X = 2 Fired_Spell_Explosion.Y = 16 Fired_Spell_Explosion.Level = 1 Fired_Spell_Explosion.Spell_number = 1 Fired_Spell_Explosion.Explosion_Frame = 12 ListAddLast(Fired_Spell_Explosion_List, Fired_Spell_Explosion) And this code is the timer which controls the frames of the explosion For Fired_Spell_Explosion = EachIn Fired_Spell_Explosion_List If Fired_Spell_Explosion.x = 0 And Fired_Spell_Explosion.y = 0 Then Continue 'If some explosion exists If Fired_Spell_Explosion_List.Count() > 0 Then 'Fast Routine 'For fireball explosion animation frame If Fired_Spell_Explosion.Explosion_Frame >= 25 Then Fired_Spell_Explosion.Explosion_Frame = 1 'Destroys the explosion and stops the sound GetPooledChannel("fireball_explosion[" + Fired_Spell_Explosion.Level + "," + Fired_Spell_Explosion.Explosion_Serial_Number + "]").Stop() 'Fired_Spell_Explosion.X = 0 'Fired_Spell_Explosion.Y = 0 ListRemove(Fired_Spell_Explosion_List, Fired_Spell_Explosion) Return EndIf 'Slow routine If Current_time >= Fired_Spell_Explosion.Explosion_Frame_Deadline_time Then Fired_Spell_Explosion.Explosion_Frame_Time_Delay = 1000 '1 second Fired_Spell_Explosion.Explosion_Frame_Deadline_time = Current_time + Fired_Spell_Explosion.Explosion_Frame_Time_Delay 'Increase the explosion animation frame Fired_Spell_Explosion.Explosion_Frame:+1 EndIf 'Continue End If Next Thank you :) |
| ||
Hi, Are you using 'New' operator each time to create a new object instance where to assign values to ? -Henri |
| ||
I cant spot the issue directly but a few things are a bit strange in your code: Why are you using bytes? Just use an int. Field Level:Byte No need to do this in your loop: If Fired_Spell_Explosion_List.Count() > 0 Then Why reset the frame if the explosion is going to be removed? Fired_Spell_Explosion.Explosion_Frame = 1 Why return in the middle of the loop? |
| ||
Fired_Spell_Explosion is a single object. This object represents a specific block of memory. Your following comment is not true : The code which adds 2 objects in the list Because you are just reassigning the fields in your single object instance. As Henri notes, you really want to create a New object for each "thing" in your list. In your example, your list is populated with the same, single object, multiple times. So, for example, the correct way here would be : Fired_Spell_Explosion = New My_Fired_Spell_Explosion Fired_Spell_Explosion.X = 2 Fired_Spell_Explosion.Y = 14 Fired_Spell_Explosion.Level = 1 Fired_Spell_Explosion.Spell_number = 1 Fired_Spell_Explosion.Explosion_Frame = 5 ListAddLast(Fired_Spell_Explosion_List, Fired_Spell_Explosion) Fired_Spell_Explosion = New My_Fired_Spell_Explosion Fired_Spell_Explosion.X = 2 Fired_Spell_Explosion.Y = 16 Fired_Spell_Explosion.Level = 1 Fired_Spell_Explosion.Spell_number = 1 Fired_Spell_Explosion.Explosion_Frame = 12 ListAddLast(Fired_Spell_Explosion_List, Fired_Spell_Explosion) The Fired_Spell_Explosion variable is assigned a new object (new block of memory), populated, and added to your list. In other notes, your test here : If Fired_Spell_Explosion_List.Count() > 0 Then Is invalid because to get to that line you've already proved there is something in your list (via For/Eachin). |
| ||
Fired_Spell_Explosion is a single object. This object represents a specific block of memory. Good spotting! |
| ||
I use If Fired_Spell_Explosion_List.Count() > 0 Then to be sure if my list have objects. But it seems is not necessary. About adding a NEW operator this is the one I always forget. I am so stupid. :P I added Fired_Spell_Explosion.Explosion_Frame = 1 might is not necessary too , because is going to be removed. This must be set when the object is generated. About Field Level:Byte I use to use bytes because most of my variables take values less than 255 and I consume only 1 byte memory. In cases I need larger variables I use int. The field level in my game will take up to 51 in the game so byte is up to 255. 51 is less 255. :) I believe I consume less memory , or I am wrong? My issue was fixed. The error was the new operator was missing :) I removed the rest unnecessary code. Thank you very very much. |
| ||
Hi Takis76 I would not worry about bytes and ints in 2015. I am not 100% what your code is designed to do but it all looked overly complicated to me so I drew up the below, *not tested* but I hope easy to read and understand. The code will create a fireball at the mouse location and update its animation and kill it once the animation is finished. FYI I use SuperStrict which is why things might look a little odd. SuperStrict // List to store Fireball Explosion objects Global LFileBalls:TList = New TList // Fireball explosion object Type TFireBallExplosion Field x:Int Field y:Int Field frame:Int Field maxFame:int=20 Field animTimer:Int Field frameTime:Int = 100 // 100ms per frame Field killMe:Int Method Update:Int() // Update animation If animTimer < Millisecs() animTimer = Millisecs()+frameTime frame = frame +1 if frame > maxFrame then killMe = True //Max frame reached so kill this object at the next opportunity End If // Can do other stuff below too such as moving x,y positions etc. End Method Method Render:Int() If killMe = True Then Return 0 // dont want to render if it needs to be removed Drawimage(blah,x,y,frame) ' // blah = your image handle End Method End Type ' // Main program loop starts here ---------------------------------------------- Repeat // Add a fireball at mouse location when left mouse button is pressed If MouseHit(MOUSE_LEFT) Local a:TFireBallExplosion = New TFireBallExplosion ' // Reference our new fireball as 'a' so we dont need to type as much a.x = MouseX() a.y = MouseY() ListAddLast(LFireBalls,a) ' // add 'a' (fireball object) to our list End If // Update and Render Fireballs, if there are no fireballs on list this will not throw an error ' // Again reference our fireball as 'a' so we dont need to type as much For Local a: TFireBallExplosion = EachIn LFileBalls a.Update a.Render Next // Remove Fireballs if flagged, if there are no fireballs on list this will not throw an error ' // Again reference our fireball as 'a' so we dont need to type as much For Local a: TFireBallExplosion = EachIn LFileBalls If a.killMe=True then ListRemove(LFireBalls,a) Next Forever |
| ||
Here's my take on it:SuperStrict Initialise() Main() Function Initialise() Graphics (800, 600) SeedRnd (MilliSecs()) SetBlend(ALPHABLEND) EndFunction Function Main() While Not AppTerminate() And Not KeyHit(KEY_ESCAPE) If MouseDown(MOUSE_LEFT) TFireBallExplosion.Create(MouseX(), MouseY()) EndIf TFireBallExplosion.UpdateAll() Cls TFireBallExplosion.RenderAll() Flip Wend EndFunction Type TGameObject Abstract Field x:Int Field y:Int Field frame:Int Field maxFame:Int=20 Field animTimer:Int Field frameTime:Int = 100 Field alpha:Float = 0 Method Update:Int() Abstract Method Render:Int() Abstract Method Kill() Abstract EndType Type TFireBallExplosion Extends TGameObject Global List:TList Function Create:TFireBallExplosion (x:Int, y:Int) If TFireBallExplosion.list = Null TFireBallExplosion.list = New TList EndIf Local fb:TFireBallExplosion = New TFireBallExplosion fb.x = x fb.y = y list.AddLast(fb) Return fb EndFunction Method Render:Int() SetAlpha(alpha) DrawOval(x, y, 10, 10) SetAlpha(1) EndMethod Method Update:Int() If alpha < 1 alpha :+ 0.01 Else alpha = 1 EndIf animTimer :+ 1 If animTimer > frameTime Kill() EndIf EndMethod Method Kill() TFireBallExplosion.List.Remove(Self) EndMethod Function UpdateAll:Int() If TFireBallExplosion.List = Null Then Return 0 For Local i:TFireBallExplosion = EachIn TFireBallExplosion.List i.Update() Next EndFunction Function RenderAll:Int() If TFireBallExplosion.List = Null Then Return 0 For Local i:TFireBallExplosion = EachIn TFireBallExplosion.List i.Render() Next EndFunction EndType Of course with coding there are a million different ways to do it :) |
| ||
@coffeedotbean The code I was posted wasn't whole , only the part I had the problem with missing New operator. @therevills I am not good in inheritance yet , abstract, extends and methods are unknown things and I am not understand them yet. I will study both of codes and I will save them. The code I use for now works. There are no examples, tutorials or books which explain inheritance and methods with blitzmax. But thank you very very much you help me. |
| ||
Instead of "ListRemove(list, variable)" you might use one less wrapper by doing list.Remove(variable) list.AddLast(variable) list.AddFirst(variable) @coffeedotbean // Update animation If animTimer < Millisecs() animTimer = Millisecs()+frameTime Your code will run into trouble on computers having an uptime of >27 days (or whatever it was until "Millisecs()" returns a negative value)). -> for this problem the interested coder might have a look at some threads in that board. bye Ron |
| ||
What is the difference from ListRemove and list.Remove? What problem do I will have about up time of 27? I didn't understand. You mean when my game remain run more than 27 days? If my game remain open for 27 day or 648 Hours , a special message will be dropped. "Hey dude , my game was perfect and you played for 648 hours in the row , but you need to get a little life too. You need to go out for clubbing or make love to your girl. The game will close now. If you like to run the game run it from program files. We apologize for wasting your time , forcing you to restart the game. " :P |
| ||
Millisecs() returns a signed integer representing the length of time, in milliseconds, since the computer was started. Once the number of milliseconds is greater than 2 billion or so (after so many days), the number will become negative (due to the way signed integers work). There are workarounds. The neatest is probably via bit masking : number & $7FFFFFFF You may say to yourself, "who leaves their PC on for so long?". An uptime on my PC here says 35 days, 23:29. So, almost 36 days since its last reboot. @list remove ListRemove() just calls list.Remove(), so you can save yourself a function call by using list.Remove() directly. |
| ||
An example of this? Current_Time = Current_Time & $7FFFFFFF Of a code like this? 'Slow routine If Current_time & $7FFFFFFF >= Fired_Spell_Explosion.Explosion_Frame_Deadline_time & $7FFFFFFF Then Fired_Spell_Explosion.Explosion_Frame_Time_Delay & $7FFFFFFF = 1000 '1 second Fired_Spell_Explosion.Explosion_Frame_Deadline_time & $7FFFFFFF = Current_time & $7FFFFFFF + Fired_Spell_Explosion.Explosion_Frame_Time_Delay & $7FFFFFFF |
| ||
Well, no. You wouldn't apply it *everywhere*, as you are needlessly doing extra maths all over the place. time = MilliSecs() & 7FFFFFFF |
| ||
It says identifier FFFFFFF Not found :) But like this compiled: Current_time = MilliSecs() & $7FFFFFFF |
| ||
@Brucey - thanks for that, I was under the impression Millisecs() returned the up time of the program not the system, I think that was a throw back to using Monkey so much. The bitshift is a neat solution. |