Classes and how to access a specific instance
Monkey Forums/Monkey Programming/Classes and how to access a specific instance
| ||
Hello all. I was in the middle of writing some code that allow my player to shoot directly at enemies and i encountered a problem: basically i don't know how to access a specific instances of the enemy class. I will try to explain without posting the code which is quite big (and i am pretty sure i am just missing a basic concept). In my on create method i initialise the player and bullets but not the enemy because i want enemies to be created at runtime based on different things. In my on update method i made a loop that check for a keypress, then create an enemy, put it in a list and start moving it. Obviously if i fire before creating the enemy the bullet won't go towards it but straight up instead. If i create one enemy the bullet will follow it properly. But....if i create more than 1 enemy the bullet get confused (obviously) and it seems to ONLY follow the last one. So i was wondering how can i make my bullet follow the last enemy, then switch to another one still present until no more enemies are there? The bullet creation happen during on update() and gets the enemy coordinates from there. Player, enemy and bullets are globals, but they get their own sets of coords and stats inside a method (in the proper class). Hope everything i said make sense. |
| ||
If every time your bullet moves it goes to the list and looks for the last enemy, then that is the one it will follow. If you want to add more enemies but have the bullets chase only the enemy they were fired at, then each bullet should individually remember which enemy it is chasing. If it finds that enemy is dead, it can switch to another target. |
| ||
Does that means that i have to somehow iterate trough the list and assign the next target? As in "if current target is dead go to the last one in the list"? Is there a way to retrieve a specific instance of an object that has been put into a list? |
| ||
it means that you need a way to reference an individual bullet by the enemy assigned to it. When the enemy is destroyed but a bullet assigned to it wasn't responsible, it should be assigned a new enemy. There are a few ways this can be achieved, but here's one: 1. Create a circular reference between bullets and their assigned enemies. A field in the enemy should contain a reference list of bullets assigned to it, and bullets should have a reference to its currently assigned enemy. 2. When an enemy is destroyed, go through its reference list and tell the bullet which destroyed it to be destroyed itself. Remove the reference in that list. For each other bullet in the reference list, assign it a new enemy target. If assignment to a new target requires iterating through all enemies or bullets or needs to comb through all the objects multiple times for some reason, then you should probably re-think your data structures a bit. |
| ||
The simplest method might be to mark targets as 'dead' when they are killed, and have the bullets check whether their current target is still alive before they move. If it isn't, they look for a new live target. |
| ||
All i am doing is define the class, create a new instance of the class (like local enemy1=new enemy) and then attach it to the list with addlast(). I don't quite understand what you mean when you say that a field in the enemy should contain a reference list of bullets. Sorry but i am still learning the language and oop in particular, and i must admit thats lists are quite difficult for me to grasp properly (also i still quite don't understand how to differentiate different versions of a class...if i create them with a local name like enemy1 would it appear as enemy1[1],enemy1[2] and so on, in the list? ). Also when checking for collisions all i am doing is to iterate trough the entire list with the "for....eachin" loop to check which one has collided and destroy it. (sorry but i am being a bit thick here!) |
| ||
well... a list is a container, where object can live inside. I try to explain it with a sample in real life: human beeings like you and me When you create a new member (=instance) it can live in a reference, like you do: Class HumanBeeing Field age%,... End Peter= New HumanBeeing Peter.Age=23 Claudia=New HumanBeeing Claudia.Age=18 But it can also live in a list: Class HumanBeeing Global WeAll:List<HumanBeeing> = New List<HumanBeeing> Field age%,... Function CreateOne(Age%) Local loc:HumanBeeing = New HumanBeeing loc.Age=Age WeAll.AddLast loc End Function End HumanBeing.CreateOne(23) HumanBeing.CreateOne(18) The second way has a big advantage: You can add as many persons as you want. To check them you can iterate the List: For local loc:HumanBeeing = EachIn HumanBeeing.WeAll Print loc.Age Next Now you can add more properties of the persons in adding more fields: Class HumanBeeing Global WeAll:List<HumanBeeing> = New List<HumanBeeing> Field age%, name$, IsFemale% Function CreateOne(Name$,Age%,IsFemal%) Local loc:HumanBeeing = New HumanBeeing loc.Name=Name loc.IsFemal=IsFemale loc.Age=Age WeAll.AddLast loc End Function End HumanBeing.CreateOne("Peter", 23, 0) HumanBeing.CreateOne("Claudia", 18, 1) For local loc:HumanBeeing = EachIn HumanBeeing.WeAll Print loc.Name + " is " + loc.Age + " years old" Next You can also create a second list to list all your ex girl friends: ... Global ExGirlFriends:List<HumanBeeing> = New List<HumanBeeing> HumanBeing.CreateOne("Peter", 23, 0) HumanBeing.CreateOne("Claudia", 18, 1) For local loc:HumanBeeing = EachIn HumanBeeing.WeAll Print loc.Name + " is " + loc.Age + " years old" If loc.IsFemale=1 ExGirlFriends.List.AddLast loc Endif Next Now Claudia is listed in two lists, but still she is only one person. If you understand this you can say that your "references" from the beginning are "lists" too, but only with one member.... So... Claudia is now a member of "WeAll" and also of "ExGirlFriends". We say Claudia has two references. But she is still only one girl. Aditional you can found a third reference, which also points to Claudia: Global MyGirl:HumanBeeing MyGirl=ExGirlFriends.Last() to comunicate, that Claudia is not longer your gilrfried you can now delete the reference... MyGirl=NULL ...but Claudia of course continue living in the lists. To kill her final... :-) you have to remove all references ExGirlFriends.Remove Claudia HumanBeeing.WeAll.Remove Claudia MyGirl=NULL An instance of a class lives as long as there is at least one reference to it. |
| ||
Lists can be a bit hard initially for somebody who isn't used to them. Bear in mind that you can certainly make your game work using arrays instead if that is your priority. But lists and arrays have their own benefits, and often one is a better choice than the other. An array can be considered as a simple block of N references to objects. A list contains an indefinite number of such references: when we access the list we can directly access the first (or last) one, but to find others we have to iterate through the list because each object reference points to the next, and the list itself does not know their addresses or even how many there are. [When you call the function List.Count() it actually has to iterate through the list and count them all - it doesn't already have the answer stored somewhere. Whereas if you call Array.Length() that value is stored already and just has to be looked up.] |
| ||
A list within a list: Now Claudia is a member of human beings. And she can have lists of things she uses in her life: Class HumanBeeing Global WeAll:List<HumanBeeing> = New List<HumanBeeing> Field age%,... Field Things:List<Thing> = New List<Thing> ... End Class Thing Field ItemName$, Price%, Weight%, Color% End Or perhaps she needs a list of exfriends. This is possible too: Class HumanBeeing Global WeAll:List<HumanBeeing> = New List<HumanBeeing> Field age%,... Field Things:List<Thing> = New List<Thing> Field ExFriends:List<HumanBeeing> = New List<HumanBeeing> ... End Now you can add "Peter" to her list of friends and you can add "Claudia" to the friends list of Peter. |
| ||
So if i understand right, if i add a field called "pointer", i can use that field as a check for the bullet to point to the right one. When i create enemies i have to make sure that every enemy instance has it's own pointer, which is always different,then the bullet can track the one i want based on whats included in the pointer field. Example: Enemy creation: for local n:int = 0 to 10 step 1 enemy1=new enemy enemy1.pointer=n enemy1.addlast(enemylist) next Bullets check: for local loc:enemy=eachin enemy(enemylist) if loc.distance <300 and loc.distance>300 followtheenemy() endif next This might be a bit heavy on computation side since its checking the distance for every object in the list... How can i tell the bullet to follow the instance that has got enemy.pointer=2 for example? Would it be: for local loc:enemy=eachin enemy(enemylist) if loc.pointer=2 followtheenemy() endif next Am i correct? |
| ||
In the previous example i was supposed to write "if loc.distance<300 and loc.distance>100" |
| ||
Every separate enemy instance is by its nature different. You don't need a separate 'pointer' variable. If you want every bullet to track the nearest enemy to it at all times, then you do have to test every bullet against every enemy (unless you are using sophisticated BSPs etc. which would probably be overkill). But is that actually what you want? Why not have each bullet remember what enemy it is targeting (no need to use a pointer)? It can check the distance periodically, and if it is too far or if the target is dead, it can then look for a new target. Each bullet just has an enemy target object (which in effect is a pointer to an enemy, not a copy or instance of one). |
| ||
As this are your first attempts to work with classes, you can do everything, which brings you to your target and is easy to understand for you... I remember, that I also used ID-numbers first for each enemy. And this I stored as a field of the bullet. Then each bullet knows "its" target enemy. some ideas to your code sample: 1. Better use a notation, where all words start with an UPPER case letter. use Enemy instead of enemy, use FollowTheEnemy instead of followtheenemy 2. the creation: Do not use local variable name like "enemy1". It may cause wrong thinking in your head. Use "locEnemy". And Use the keyword Local to make clear, that this variable is a local one: For local n:int = 0 to 10 step 1 Local locEnemy = New Enemy locEnemy.pointer=n EnemyList.AddLast locEnemy next 3. If your list of enemies is inside the Enemy class the correct call is "Enemy.EnemyList" and not "enemy(enemylist)" 4. the bullet check: Are you sure, that the distance can be a property of the Enemies? Normaly the distance is a flexible value and caused by distance between bullet and enemy... I would think the distance is something like... For local loc:Enemy=EachIn Enemy.EnemyList local Distance= Abs(bullet.X - loc.X) If Distance <300 ... |
| ||
Thanks for the suggestion Midimaster, i do nearly everything you said, its just that when i post i tend to enter "lazy mode" and forget proper formatting! I quite understand everything has been explained here(quite...) and i understand how to locate different instances. I think the problem might reside in how i create instances in the first place! For example to make bullets i have created a button, and everytime it get hit a new bullet get created (during the onupdate method) like this: class bullet field x,y,speed,power method init() x=SuchAndSuch y=SuchAndSuch speed=SuchAndSuch power=SuchAndSuch addLast(BulletList) end method method render() 'render code here end method endclass if ButtonHit() bullet1=new bullet bullet1.init() endif As you can see there's no way i can tell the difference between them if i press the button 10 times(for example) because all the fields are the same. Also i don't understand when Gerry says that each bullet can have an enemy list. |
| ||
ah! the Init() should not be a Method(), but a Function() of your Class. Functions() inside Classes are not connected to a certain instance. You can call them to do things regardings all instances of this class... There is no need to differ between the bullets from outside. In a good organized class the bullets will care about themselves! You awoke one from outside and add it to the class intern list. That's all! From this moment it lives independent from outside and interacts with the surrounding self organized during its live cycle. Your job is to add methods, where is descriped how to move, check and interact with other classes. At the end it will even die self organized. Where do you see a need for accessing them from ouside? What is the job of your bullets? Fly straight ahead and kill something, which comes in the vincity of it? Like a canon ball? Or targeting one enemy and follow him, even when he moves? Like IR missiles? Or targeting the the nearest enemy, even when this means to change suddenly from one enemy to the next during the hunt? Like dogs in the park? |
| ||
I see Midimaster has pointed out the use of Functions. I would suggest that you read some of the tutorials on classes. There are some video tutorials by invaderjim in the monkey tutorials. Well one of the project that I'm thinking of doing involves homing projectiles. And I've thinking of how I could implement that kind of thing. The idea would be to have two lists one for bullet objects and another list for the enemy objects. Now the idea would be for each bullet to have a reference to the targeted enemies own instance within the enemy list. And the targeted enemy instance to know that it's been targeted so two bullets don't chase the same target. Rough outline classes with pseudo code idea blah,blah If I come up with something that works I'll post it in the code section so others can post comment etc. But it could be a few weeks as I'm suppose to be studying for my Domestic Gas Engineering Course. |
| ||
Hello. At the moment i have 2 different movements.. one is straight ahead and the other is targeted....problem is i don't understand how to give a target to it since enemies are created during runtime in the same way as the bullets. Thanks for pointing me on how to put functions inside a class,very useful. Really a class should be completely self organised, and that make sense. Now just trying to get my head around on how to interact with another class (enemy...so it can target a specific one). I thought i understood classes but obviously i haven't;i am not particularly upset about it because the more i learn the more possibilities opens to me. Btw i am programming as a hobby in my spare time, so i am not particularly rushing to publish a game. Would be cool to have something mine in a store though :P |
| ||
A possible way of targeting an enemy is to search for the best one in the moment of the birth of the bullet. Then keep this enemy in mind as a field of the bullet and now follow this enemy until death. The bullet finds the best enemy with calling a function of the Enemy Class: Class Bullet Global BulletList:List<Bullet>= new List<Bullet> Field x,y,..., MyEnemy:Enemy Function Init() loc=New Bullet loc.StartUp BulletList.AddLast loc End Function Method StartUp() x=SuchAndSuch y=SuchAndSuch ... MyEnemy=Enemy.FindBest(x,y) End Method ... Method Follow() x = x + Sgn(MyEnemy.x-x) ' one step in direction to my enemy y = y + Sgn(MyEnemy.y-y) End Method ... End Class Enemy Global EnemyList:List<Enemy>= new List<Enemy> Field x,y,... Function Init() loc=New Enemy loc.StartUp EnemyList.AddLast loc End Function Method StartUp() x=SuchAndSuch y=SuchAndSuch End Method Function FindBest:Enemy(X#,Y#) local TheBest:Enemy For local loc:Enemy =EachIn EnemyList If Abs(loc.x-X)<100 And Whatever.... TheBest=loc Endif Next Return TheBest End Function End |
| ||
I was suggesting that each bullet could store a reference to a single enemy, not a list. That's what Midimaster has implemented in his code above. The field MyEnemy in each bullet allows the bullet to remember what enemy it is currently going for. It only needs to look up the list when there is something wrong with that enemy, e.g. it is too far away, or already dead. |
| ||
Thanks, you gave me what i completely missed until now, and thats the "return" at the end of a function.... I must admit i never quite understood how to return things from inside a function, but that make sense. Thanks for the help. Btw how is your app doing? Did you have any more compatibility issues with latest Androids? (sorry for derailing..) |
| ||
are you talking about my app or garry's? |
| ||
Sorry i should have explained..i meant yours midimaster. Also before i meant thanks to everybody (damn "internet lazy mode"!) |
| ||
(very difficult to post in between working shifts on a phone) |
| ||
So you talk about my "Score-Trainer"? Well,.. the app is on GooglePlay and I sell now also a pro version. I sell so many, that I only need 15.000 more months like the last one to become millionaire.... the problems on 4.xx were caused by a different handling of orientation changes. Mark found out, what the reason was and changed something in the V70 now. So now nobody should have those problems again. A second source of problems is the different way of loading resources on 4.xx. My resume on this: Everybody should now switch to async loading to prevent problems. A third handicap of the app is the disability of displaying texts sharp and fast and stylish like other text. I tried to start a discussion here about the need of a html-text displayer, but had no resonance. Seems that I am the only one, who offers manuals and long texts to the users. So I keep on fighting every day... |