AI

BlitzMax Forums/BlitzMax Programming/AI

Cruis.In(Posted 2006) [#1]
I had a topic deleted "best way?"

dunno why, didn't get to see if any responses. I was wondering how to handle the AI ships in the enemyship list to shoot at the player, using the EBullet Type or Emissile Type


SculptureOfSoul(Posted 2006) [#2]
oh man, I had posted like 5 replies to that. ::Grumble::

my best advice, start using STRICT immediately.


Perturbatio(Posted 2006) [#3]
http://www.blitzbasic.com/Community/posts.php?topic=65100#726843


bradford6(Posted 2006) [#4]
do you have some code?


Scott Shaver(Posted 2006) [#5]
http://www.blitzbasic.com/Community/posts.php?topic=55711

check out the PursuitTest program.


Cruis.In(Posted 2006) [#6]
i posted it in the other thread GAWD.


Cruis.In(Posted 2006) [#7]
ok basically what is happening is, I have several enemy ship objects.

I want them each to fire a missile at the player, (obviously based on certain conditions)

problem I am having is, only ship at a time is able to fire, and it is the last ship I instance. You press a button to add in enemies. So as you press, the new enemy picks up the firing. And you see the missile coming from that ship instead.

Interestingly as well, the amount of damage being done by this "single" missile hitting your ship, is equivelant to the amount of enemy ships you have. FOR example, if a missile does 10 damage from one hit, if you have 5 enemeies instances, the missile that is fired does 50 damage.

enemy missile Type:
Type Etmissile Extends tmissile
	Field lastfired%
	Global list:TList = CreateList()
	
	
	Function FireTorp()			
		'creates the missile object and fires it really.
		Firemissile (ship.x , ship.y)   				
	End Function
	
	
	Function Update()
		'Local eachenemy:tenemyship
		For emissile:etmissile = EachIn List 
			emissile.age :+ 1		
			emissile.frame:+ 1	
			If emissile.frame = 30 Then emissile.frame = 0
				
			emissile.x :+ Sin(emissile.angle) * (emissile.Speed)' + eachEnemy.current_speed *delta
			emissile.y :- Cos(emissile.angle) * (emissile.Speed)' + eachEnemy.current_speed *delta
				
			'remove missile if is older than 150
			If emissile.age > 150 
				PlaySound(missileExplosionFx:TSound)
				RemoveLink emissile.link
			End If 			 
		Next
	End Function 

	
	Function draw()
		For Local all:etmissile = EachIn list
			SetScale 1 + newscale, 1.0 + newscale'scales the missilees according to zoom
			SetBlend ALPHABLEND
			SetColor(255,255,255)
			MidHandleImage (all.photonImage)
			DrawImage(all.PhotonImage,all.x,all.y,all.frame)
		Next
	End Function
	'=======================================================================================
	'create a new object missile based on the type weapon, adds each to a list, and appends
	'deletes from the list as each missilees lifetime goes to 0 or as you fire.
	'=======================================================================================
	Function Firemissile(x#,y#)
		'controls how rapid you can fire missilees. 100= 100 split seconds
		If (MilliSecs() - emissile.lastFired) < 600 Then Return 				
					
		Local emissile:etmissile = New eTmissile			
		PlaySound(sound.CurrentmissileSound)
		emissile.frame = 0		
		
		For Local eachenemy:tenemyship = EachIn tenemyship.eshiplist
			emissile.angle = ATan2( eachEnemy.x - ship.x , ship.y - eachEnemy.y )+180 
			emissile.lastFired = MilliSecs() 
			emissile.x = eachEnemy.x + (Sin(emissile.angle))  * delta'so torp orgin from ship
			emissile.y = eachEnemy.y + (Cos(emissile.angle))  * delta
		Next
		
 		emissile.age = 0
		emissile.link = ListAddLast(list,emissile)			
	End Function
	
	
		
End Type





This is called in an AI() function in the main loop

'fires enemy missiles 
Function Fire()
	Local weaponRange = 600
	
	'fire if distance is less than weapon range
	For Local eachenemy:tenemyship = EachIn tenemyship.eshiplist	
		If ListIsEmpty(tenemyship.eshiplist) = False  
			If ListIsEmpty(ship.shiplist) = False
				If ship.iscloaked = False
					If EachEnemy.iscloaked = False
						If eachenemy.distance < weaponRange
							etMissile.fireTorp()	
						End If 
					End If 
				End If
			End If 
		End If
	Next
End Function





in the main loop "AI()" is before these:


etMissile.update()
etMissile.draw()

of course I want each enemy ship to fire on the player. so hence the for each loop in the fire() function

there there is a for each loop in the FireMissile() function of the enemy missile type because, the missile must originate from the position of the enemy ship that fired it, and the angle/direction of the missile etc, must be calculated based from the ship that fired it, hence for that piece of code.

fire missile is basically a create() function


SculptureOfSoul(Posted 2006) [#8]
Well, that's some different code than you posted last time, but I'll try to help.

You're mistaken by referencing "Ship.X" and "Ship.Y" in your fire torpedo function. You are also mistaken by referencing "EachEnemy" in your fire missile function. Neither Ship nor EachEnemy exist within the scope that you are referencing them. Actually, I'm assuming ship is a global value that represents your players object. But why then does your fire torpedo function always use Ship.X and Ship.Y? Shouldn't it take the X and Y of the firing ship as parameters?

etMissile.fireTorp()	


Why are you calling fireTorp, a function, as a method?

What you need to do is to give the enemy type an Update method. There's no reason that code that only acts on a single type should be a function. No, whenever you have code that only modifies a single type, it should probably be a method of that type. So then, each frame you do this
local EachEnemy:TEnemy = Eachin EnemyShipList
 EachEnemy.Update()


The actual code of update should handle repositioning and drawing the ship, and then evaluating the test conditions to see if the ship should fire a missile. Also, make sure give the enemy type a "LastFired" field. There is no reason that field should belong to the missile. Instead, each enemy should track when it was last fired. The way you have it now, the missile type itself is storing the last time it was shot (presumably in a global variable given the problems you are having) and hence, whenever ANYONE shoots it prevents everyone else from firing until that time has passed.

Lastly, make the FireMissile() a method of the TEnemy type. So now you've got an Update() method inside of the TEnemy, and if the test conditions pass inside of Update() it calls FireMissile(), also a method inside of TEnemy. FireMissile needs to reference the X and Y field of the object calling it, which is trivial once you've made it a method of that type.

Lastly, you can get rid of this line in your last block of code
If ListIsEmpty(tenemyship.eshiplist) = False  


If eshiplist is empty, the "For Eachin" loop simply won't execute. Hence, you are wasting cycles testing for a condition that will never be true at the time of testing.

Lastly, and this is the most important thing you can do to help improve your coding and catch random bugs is to use STRICT. In fact, I'm not trying to be rude, but I'm not going to offer anymore help until you start using it. Not using strict makes it exponentially harder to debug, especially when you only provide snippets of code. It's not worth my (or others) effort to try and help with your code when you aren't using strict as it's simply too hard to tell what might be the problem (for instance, you can reference variables that should be out of scope, but how can we tell?)


Cruis.In(Posted 2006) [#9]
the X and Y values of fire torpedo, are where to aim them at, hence ship.x, ship.y

i am using strict dude

why are you calling firetorp as a method


because that's calling the function of the type, that way accesses a types functions without having to use a specific object of the type.


tonyg(Posted 2006) [#10]
You seem to be accessing emissile both globally and locally within the firemissile function.
That can't be right... can it?
I also think that the firetorp function doesn't seem to do anything than simply call firemissile.
What does it add?
I'm not saying either of these will solve your problem but it's poor coding and that can lead to 'odd' problems like the ones you are having.


Cruis.In(Posted 2006) [#11]
right, well I created the firetorp function as a test actually. dont remember but it was a test to see if a theory i had worked.

also the emissile globally and locally. there is in fact a globaled emissile, declared in globals, and as you can see the firemissile function has a local emissile:tmissile in it...so yes it is possible that is causing an error.

I have been trying to change it but the problem I am running into is that how would I set the fields of each missile object that need to be set in that firemissile function?

because the enemy ships are at different positions each missile that gets fired needs to calculate the direction to fire the missile at, based off of where the enenmy ship that is firing at you is located. That's why I placed a for eachin loop there... it actually isn't correct as it is, its saying that for each ship in the enym ship list, and then goes on to only calculate for a single missile object. because you should use the local variable you declared in the for each in loop.

my main problem is that I am basing the logic of this emissile type off of the missile type the player uses to fire missiles, which works flawlessly. and since there is only one player, i wouldnt run into the problems I am running into here because i dont have to factor in firing from multiple instances of my player ship as I would with the enemy fleet of ships.

WHich leads me to the only conclusion, a re write of the code to handle the enemy missiles, forgetting the previous type, and taking into account from the beginning the fact that I have multiple instances to be concerned with regards to firing.

Is it neccessary though that I need a separate missile type for the enemy? The reason I made it separate and to extend the type used for the player's missiles is because I ran into trouble properly updating enemy missiles with the functions in the other type, hence a new type and ovverriding functions.

the missiles I might add work well with only one enemy ship instanced.


tonyg(Posted 2006) [#12]
because the enemy ships are at different positions each missile that gets fired needs to calculate the direction to fire the missile at, based off of where the enenmy ship that is firing at you is located
Not a problem because you (or at least your list of tenemy) knows the position of each tenemy and you know the position of the player ship.
When it's time for a tenemy to fire simply has a fire function/method within your tenemy type passing the tenemy x/y and the global playership x/y and use that information to call a tmissile.create(x,y,playerx,playery) function.
Once the tenemy has fired it's missile it can forget about it or, if you need to know which tenemy fired add it to a list of tmissiles specifically for that tenemy.
Once you have a list of tmissiles that are active the tmissile.update and tmissile.draw function can handle their movement.
This code doesn't have the ability to target anything but you can add the code for that...

It's quick and dirty... as always.


Cruis.In(Posted 2006) [#13]
yeah? well your quick dirty one helped last time, remember the explosions? :)
thanks Ill have a look when I leave work.


Cruis.In(Posted 2006) [#14]
having a more concentrated look at home with my code. the problem with the firemissile function is that it cannot see the values of the x and y of the ship that is firing it because there is no ship visible to that function.

if i do a
local enemyship:tenemyship = new tenemyship

sure it can see an enemy ship, but it will be seeing an entirely new enemy ship and not even the one or ones firing the missiles.


REDi(Posted 2006) [#15]
Why is Fire a function? it should really be a method of tenemyship, Perhaps with a target parameter as well.


Cruis.In(Posted 2006) [#16]
you mean fireMissile() should be in tenemyship

guess like i said couple posts above, have to rewrite keeping in mind its the tenemyship AI thats firing, which is going to be different from anything to do with one player controlled object.


Cruis.In(Posted 2006) [#17]
ok redI I was trying that before, didn't work out, tried it again and saw logically why it didnt work when I tried it before. minor adjustment and voila.

will post exactly what solved it later.
thanks for all the help guys.


tonyg(Posted 2006) [#18]
the problem with the firemissile function is that it cannot see the values of the x and y of the ship that is firing it because there is no ship visible to that function.


Which is why, in your case, I suggested having your playership global.


Cruis.In(Posted 2006) [#19]
and so it could have stayed there. then there is the x and y of each enemy ship that the missile must originate from.


tonyg(Posted 2006) [#20]
and so it could have stayed there.

Not sure what you mean by that. What could have stayed where?

then there is the x and y of each enemy ship that the missile must originate from.


which you take from your Tmissile x/y fields.


SculptureOfSoul(Posted 2006) [#21]
Make FireMissile() a method of TEnemyShip. Call it with the parameters of the target locations X,Y. Then, inside of the FireMissile function, you can instantiate a TMissile with the target X,Y and the X,Y fields of the EnemyShip that is creating it.

i.e.
Type TEnemyShip
field x:int
field y:int
field lastfired:int

method FireMissile( targetx:int, targety:int )
if( millisecs() < lastfired + fireinterval)
 return 'enough time hasn't elapsed yet
else
 local newMissile:TMissile = new TMissile
 newMissile.TargetX = targetx
 newMissile.TargetY = targety
 newMissile.x = x 'this assigns the missile the x position of the parent ship
 newMissile.y = y 'this assigns the missile the y position of the parent ship
 GlobalMissileList.Addlast(newMissile) 
 lastfired = millisecs()
endif
EndMethod
EndType