Classes and how to access a specific instance

Monkey Forums/Monkey Programming/Classes and how to access a specific instance

mteo77(Posted 2013) [#1]
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.


Gerry Quinn(Posted 2013) [#2]
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.


mteo77(Posted 2013) [#3]
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?


Nobuyuki(Posted 2013) [#4]
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.


Gerry Quinn(Posted 2013) [#5]
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.


mteo77(Posted 2013) [#6]
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!)


Midimaster(Posted 2013) [#7]
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.


Gerry Quinn(Posted 2013) [#8]
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.]


Midimaster(Posted 2013) [#9]
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.


mteo77(Posted 2013) [#10]
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?


mteo77(Posted 2013) [#11]
In the previous example i was supposed to write
"if loc.distance<300 and loc.distance>100"


Gerry Quinn(Posted 2013) [#12]
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).


Midimaster(Posted 2013) [#13]
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 ...



mteo77(Posted 2013) [#14]
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.


Midimaster(Posted 2013) [#15]
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?


dawlane(Posted 2013) [#16]
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.


mteo77(Posted 2013) [#17]
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


Midimaster(Posted 2013) [#18]
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



Gerry Quinn(Posted 2013) [#19]
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.


mteo77(Posted 2013) [#20]
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..)


Midimaster(Posted 2013) [#21]
are you talking about my app or garry's?


mteo77(Posted 2013) [#22]
Sorry i should have explained..i meant yours midimaster.
Also before i meant thanks to everybody (damn "internet lazy mode"!)


mteo77(Posted 2013) [#23]
(very difficult to post in between working shifts on a phone)


Midimaster(Posted 2013) [#24]
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...