Posted: 12th Jan 2022 6:18
Help,

I am using spheres for the object and am wondering how I would do collision detection? I already did
+ Code Snippet
SetObjectCollisionMode(sphere, 1)
but I don't see any commands in the 3D section of the documentation for actually checking it. A little help please?
Posted: 12th Jan 2022 8:12
That function enables the objects ability to be hit by ray (RayCasting) look at ObjectSphereCast
Posted: 12th Jan 2022 8:42
Hi OWl. Take a look here
https://forum.thegamecreators.com/thread/228144
Posted: 12th Jan 2022 9:38
I all you need is to know whether two spheres are overlapping then the simplest solution is a basic distance check - if the distance between the centre points is less the the combined radii of the spheres then they must be overlapping. If it's greater then they cannot possibly be overlapping.
+ Code Snippet
function GetSphereCollision(s1, s2)
	x1# = GetObjectX(s1)
	y1# = GetObjectY(s1)
	z1# = GetObjectZ(s1)
	x2# = GetObjectX(s2)
	y2# = GetObjectY(s2)
	z2# = GetObjectZ(s2)
	r1# = GetObjectSizeMaxX(s1)
	r2# = GetObjectSizeMaxX(s2)
	d# = sqrt((x1#-x2#)*(x1#-x2#)+(y1#-y2#)*(y1#-y2#)+(z1#-z2#)*(z1#-z2#))
endfunction d# < r1#+r2#


With the above function you pass the object ID's of the two spheres you want to check for collision. If they collide, the function returns 1. If the don't collide it will return 0.
Posted: 12th Jan 2022 12:50
https://forum.thegamecreators.com/thread/225596#msg2651674
Posted: 13th Jan 2022 1:01
If you are using the physics engine, i.e. your spheres are dynamic objects, the collision detection has already been checked for you.

GetObject3DPhysicsFirstContact(object1_ID) will return 1 if collision occurred in the last step. Use GetObject3DPhysicsContactObjectB() to get the object ID of what object1_ID hit. Iterate through the list of hit objects with GetObject3DPhysicsNextContact().

Edit - note there is a separate help section for 3D physics https://www.appgamekit.com/documentation/Reference/3DPhysics.htm
Posted: 13th Jan 2022 3:46
similar to box2d, there are advantages to using bullet (3d) physics for less than a full-blown physics world which is why i nudged toward the "light" bullet example.

but, since you haven't expounded on how many objects need to be tested each frame, it's hard (for me) to offer more.
Posted: 15th Jan 2022 3:15
This only works if I comment out 2 lines, so only 1 object is deleted.

+ Code Snippet
function checkForCollision()
	for bulletCounter = 0 to bullet.length
		for minionCounter = 0 to cpuMinion.length
			if GetSphereCollision(bullet[bulletCounter].id, cpuMinion[minionCounter].id) = 1
				DeleteObject(bullet[bulletCounter].id)
				DeleteObject(cpuMinion[minionCounter].id)
				bullet.remove(bulletCounter)
				cpuMinion.remove(minionCounter)
			endif
		next minionCounter
	next bulletCounter
endfunction


So, for example:

+ Code Snippet
function checkForCollision()
	for bulletCounter = 0 to bullet.length
		for minionCounter = 0 to cpuMinion.length
			if GetSphereCollision(bullet[bulletCounter].id, cpuMinion[minionCounter].id) = 1
				//DeleteObject(bullet[bulletCounter].id)
				DeleteObject(cpuMinion[minionCounter].id)
				//bullet.remove(bulletCounter)
				cpuMinion.remove(minionCounter)
			endif
		next minionCounter
	next bulletCounter
endfunction


-or-

+ Code Snippet
function checkForCollision()
	for bulletCounter = 0 to bullet.length
		for minionCounter = 0 to cpuMinion.length
			if GetSphereCollision(bullet[bulletCounter].id, cpuMinion[minionCounter].id) = 1
				DeleteObject(bullet[bulletCounter].id)
				//DeleteObject(cpuMinion[minionCounter].id)
				bullet.remove(bulletCounter)
				//cpuMinion.remove(minionCounter)
			endif
		next minionCounter
	next bulletCounter
endfunction


Works but, having both active only makes 1 work. Any ideas?
Posted: 15th Jan 2022 14:34
Wild guess with quick look but should you be stepping backwards in your bullet loop?

+ Code Snippet
for bulletCounter = bullet.lengh to 0 step -1
Posted: 15th Jan 2022 21:03
+ Code Snippet
for bulletCounter = bullet.lengh to 0 step -1


Nope that didn't work, here is the source code.
Posted: 18th Jan 2022 0:22
Changed it to:

+ Code Snippet
function checkForCollision()
	for bulletCounter = bullet.length to 0 step -1
		for minionCounter = cpuMinion.length to 0 step -1
			if GetSphereCollision(bullet[bulletCounter].id, cpuMinion[minionCounter].id) = 1
				DeleteObject(bullet[bulletCounter].id)
				DeleteObject(cpuMinion[minionCounter].id)
				bullet.remove(bulletCounter)
				cpuMinion.remove(minionCounter)
			endif
		next minionCounter
	next bulletCounter
endfunction


Still no dice. Maybe there is a bug in the compiler itself?
Posted: 18th Jan 2022 1:07
Are there multiple minions per bullet or vice versa? Maybe use a flag to 'exit' the minion counter loop? This wouldn't be a compiler error, I'd take another look at your method to see if you should have nested loops like this.

Have minions in your first loop. Then go through all the bullets, each bullet that hits a minion gets deleted and add a flag to the minion that it's been hit. Once the bullet loop is done delete the minion and carry on with the minion loop.

+ Code Snippet
function checkForCollision()
  for minionCounter = cpuMinion.length to 0 step -1
      for bulletCounter = bullet.length to 0 step -1
      
            if GetSphereCollision(bullet[bulletCounter].id, cpuMinion[minionCounter].id) = 1
                DeleteObject(bullet[bulletCounter].id)
              
                bullet.remove(bulletCounter)
               cpuMinion[minionCounter].hit = 1    //New flag for minions
            endif
        next bulletCounter
       if (cpuMinion[minionCounter].hit = 1)
            DeleteObject(cpuMinion[minionCounter].id)
           cpuMinion.remove(minionCounter)
      endif
    next minionCounter
    
endfunction

Posted: 18th Jan 2022 1:19
i've skimmed your code and, personally, i think you're overdoing it with "collision".

in your PvZ-type, the bullets and enemies stay in the same row so, there's no need for Z axis. they also stay at the same height, so nix Y. all you need to do is compare X components of bullets in a given row vs enemies in the same row to determine "hit".

in the end, i'd compare X of all bullets in a given row vs enemies in the same row, at most.

now, with your nested loops, it appears you're deleting objects in 1 loop that may be part of the other. removing them as you're doing is going to break 1 loop or the other (if i'm seeing things right), so don't. i see JD has posted as i'm writing this and yes, don't nest the loops.

in the template that i posted, i DoEnemies() then later DoTowers(). DoTowers has immediate hit checks vs an actual "moving" bullet.

i don't know exactly how yours works but DoEnemies() then DoBullets() (with DoTowers() in there somewhere?).
Posted: 22nd Jan 2022 14:44
I fixed the overly complicated collision checks and double loop issue yet, the problem persists. Also, now all the minions are destroyed instead of just the one with the collision. I think this is a timing issue cause if the defense is put in the back both all the bullets and minions are destroyed. If you put the defense in the front only all the minions are destroyed.
Posted: 23rd Jan 2022 4:17
Tried the code above to no success. Tried altering it so all objects were deleted after a hit flag was set:

+ Code Snippet
function checkForBulletCollision()
for minionCounter = 0 to cpuMinion.length
	for bulletCounter = 0 to bullet.length

		if GetSphereCollision(bullet[bulletCounter].id, cpuMinion[minionCounter].id) = 1
			bullet[bulletCounter].hit = 1
		endif
		
		if(bullet[bulletCounter].hit = 1)
			DeleteObject(bullet[bulletCounter].id)

			bullet.remove(bulletCounter)
			cpuMinion[minionCounter].hit = 1
		endif

	next bulletCounter
	if (cpuMinion[minionCounter].hit = 1)
		DeleteObject(cpuMinion[minionCounter].id)
		cpuMinion.remove(minionCounter)
	endif

next minionCounter

endfunction

Still no success

UPDATE 1: Changed it so object cleanup happens in another function:

System.agc

new
+ Code Snippet
function cleanupObjects()
	for counter = 0 to playerDefense.length
		if(playerDefense[counter].dead = 1)
			DeleteObject(playerDefense[counter].id)

			playerDefense.remove(counter)
		endif
	next counter
	
	
	for counter = 0 to bullet.length
		if(bullet[counter].dead = 1)
			DeleteObject(bullet[counter].id)

			bullet.remove(counter)
		endif
	next counter
	
	for counter = 0 to cpuMinion.length
		if(cpuMinion[counter].dead = 1)
			DeleteObject(cpuMinion[counter].id)

			cpuMinion.remove(counter)
		endif
	next counter
endfunction


collision.agc

updated
+ Code Snippet
function checkForBulletCollision()
for minionCounter = 0 to cpuMinion.length
	for bulletCounter = 0 to bullet.length

		if GetSphereCollision(bullet[bulletCounter].id, cpuMinion[minionCounter].id) = 1
			bullet[bulletCounter].dead = 1
			cpuMinion[minionCounter].dead = 1
		endif

	next bulletCounter
next minionCounter

endfunction
Posted: 23rd Jan 2022 17:10
updated

does that mean yout have it working correctly?
Posted: 24th Jan 2022 0:04
Quote: "updated"


does that mean yout have it working correctly?


Nope
Posted: 24th Jan 2022 4:03
using the method that i suggested before:
+ Code Snippet
// Project: Tap Tower
// Created: 2022-01-23
// By: Virtual Nomad
// show all errors
SetErrorMode(2)

// set window properties
SetWindowTitle( "Tap Tower" )
SetWindowSize( 1280,720, 1 )
SetWindowAllowResize( 1 )

// set display properties
SetVirtualResolution( 1280,720)
SetOrientationAllowed( 1, 1, 1, 1 )
SetSyncRate( 30, 0 ) 
SetScissor( 0,0,0,0 )
UseNewDefaultFonts( 1 ) 

Create3DPhysicsWorld()

SetShadowMappingMode(3)
SetSunActive( 1)

Type Tower
	ID, NextShot#, RateOfFire#
EndType
	
GLOBAL Towers as Tower []

Type Enemy
	ID, Health
EndType

GLOBAL Enemies as Enemy []

GLOBAL LastSpawn#, Cash
Cash = 50

GLOBAL Board as Integer [9,4]
MakeBoard()

GLOBAL Shots as Integer []

SetCameraPosition(1,144,150,-220)	:	SetCameraLookAt(1,144,0,96,0)

do

    If GetRawKeyState(27) then End
    
    If LastSpawn# + 2.0 <= Timer()
		SpawnEnemy()
		LastSpawn# = Timer()
	Endif
	
	DoTowers()
	DoShots()
	If Enemies.Length > -1 then DoEnemies()   
	
    Print( "Cash: " + STR(Cash) )
	If Cash >= 10 and Towers.Length < 14 then Print("Tap to Build Tower")
	Step3DPhysicsWorld()
    Sync()
loop

Function SpawnEnemy()
	ThisEnemy = CreateObjectBox(10,10,10)	:	SetObjectColor(ThisEnemy,255,0,0,255)
	SetObjectCastShadow(ThisEnemy,1)
	SetObjectPosition(ThisEnemy,300,10,Random(0,4)*32)
	Create3DPhysicsKinematicBody(ThisEnemy)
	AddEnemy as Enemy
		AddEnemy.Health = 30	:	AddEnemy.ID = ThisEnemy
	Enemies.Insert(AddEnemy)
EndFunction

Function DoEnemies()
	For x = Enemies.Length to 0 Step -1
		ThisEnemy = Enemies[x].ID
		MoveObjectLocalX(ThisEnemy,-1)
		If GetObjectX(ThisEnemy) <= -16.0
			If Cash >= 10 then DEC Cash, 10
			Delete3DPhysicsBody(ThisEnemy)
			DeleteObject(ThisEnemy)
			Enemies.Remove(x)
		EndIf
	Next X
EndFunction

Function DoTowers()
	If GetPointerPressed() and Towers.Length < 14 and Cash >= 10
		DEC Cash, 10
		ThisTower = CreateObjectSphere(20,10,10)
		SetObjectCastShadow(ThisTower,1)
		Empty = 0
		Repeat //Random Placement into empty Tile
			ThisX = Random(0,2)	:	ThisZ = Random(0,4)
			If Board[ThisX,ThisZ] = 0
				Empty = 1
				Board[ThisX,ThisZ] = ThisTower
			EndIf
		Until Empty = 1
		SetObjectPosition(ThisTower,ThisX*32,10,ThisZ*32)
		AddTower as Tower
			AddTower.ID = ThisTower
			AddTower.RateOfFire# = 5.0
			AddTower.NextShot# = Timer()
		Towers.Insert(AddTower)
	EndIf

	If Towers.Length > -1
		For x = 0 to Towers.Length
			If Towers[x].NextShot# <= Timer()
				ThisTower = Towers[x].ID
				ThisShot = CreateObjectSphere(5,5,5)
				Create3DPhysicsKinematicBody(ThisShot)
				SetObjectShapeSphere(ThisShot)
				SetObjectCastShadow(ThisShot,1)
				SetObjectPosition(ThisShot,GetObjectX(ThisTower),GetobjectY(ThisTower),GetObjectZ(ThisTower))
				Towers[x].NextShot# = Timer() + Towers[x].RateOfFire#
				Shots.Insert(ThisShot)
			Endif
		Next x
	EndIf
EndFunction

Function DoShots()
	Vec3 = CreateVector3()
	For x = Shots.Length to 0 Step -1
		ThisShot = Shots[x]
		MoveObjectLocalX(ThisShot,0.5)
		If GetObjectX(ThisShot) >= 300
			Delete3DPhysicsBody(ThisShot)
			DeleteObject(ThisShot)
			Shots.Remove(x)
		ElseIf Enemies.Length > -1
			For Enemy = Enemies.Length to 0 Step -1
				ThisEnemy = Enemies[Enemy].ID
				If GetObjects3DPhysicsContactPositionVector( ThisShot, ThisEnemy, Vec3 ) = 1
					Enemies[Enemy].Health = Enemies[Enemy].Health-10
					If Enemies[Enemy].Health <= 0
						INC Cash, 10
						Delete3DPhysicsBody(ThisEnemy)
						DeleteObject(ThisEnemy)
						Enemies.Remove(Enemy)
					EndIf
					Delete3DPhysicsBody(ThisShot)
					DeleteObject(ThisShot)
					Shots.Remove(x)
					Exit
				EndIf
			Next Enemy
		EndIf
	Next x
EndFunction

Function MakeBoard()
	Toggle = 1
	For x = 0 to 9
		For z = 0 to 4
			Tile = CreateObjectBox(32,4,32)	:	SetObjectPosition(Tile,x*32,-2,z*32)
			SetObjectReceiveShadow(Tile,1)
			If Toggle = 1 Then SetObjectColor(Tile,0,255,0,255) Else SetObjectColor(Tile,0,192,0,255)
			Toggle = -Toggle
		Next z
	Next x
EndFunction



If you make the Towers Kinematic Bodies, you can use a similar "collision" routine for Enemy vs Towers (in DoEnemies()). Ie, that part...
+ Code Snippet
For Enemy = Enemies.Length to 0 Step -1
	ThisEnemy = Enemies[Enemy].ID
	If GetObjects3DPhysicsContactPositionVector( ThisShot, ThisEnemy, Vec3 ) = 1
		Enemies[Enemy].Health = Enemies[Enemy].Health-10
		If Enemies[Enemy].Health <= 0
			INC Cash, 10
			Delete3DPhysicsBody(ThisEnemy)
			DeleteObject(ThisEnemy)
			Enemies.Remove(Enemy)
		EndIf
		Delete3DPhysicsBody(ThisShot)
		DeleteObject(ThisShot)
		Shots.Remove(x)
		Exit
	EndIf
Next Enemy

...using the almighty GetObjects3DPhysicsContactPositionVector().

otherwise, sorry i didn't try your method. once i find something that works, i tend to stick with it

meanwhile, that would be considered "brute force" in checking each shot vs all enemies (until it hits one). the whole thing can be simplified setting linear velocities for enemies and shots (once) vs MoveObjectLocalX() each frame and simply checking for Contact(s).

alas, i wanted this to remain similar to what i think you were doing. lucky for us, Brute Force works fine for smaller stuff like this
Posted: 25th Jan 2022 0:12
I altered my code to use the almighty GetObjects3DPhysicsContactPositionVector() function my adding the required calls to Create3DPhysicsWorld(), Create3DPhysicsKinematicBody(object), SetObjectShapeSphere(), SetObjectShapeBox()
and using the above function for the actual collision, and it still isn't working.
Posted: 25th Jan 2022 1:40
you need to add Step3DPhysicsWorld() just before Sync() just like i showed you in the last code.

i made a couple of other changes to the code before i realized that you'd missed that.

this, for one:
+ Code Snippet
for minionCounter = cpuMinion.length to 0 step -1

	// get the id of the minion
	minionID = cpuMinion[minionCounter].id ///THIS NEEDS TO BE IN THE MINION LOOP
	
	for bulletCounter = bullet.length to 0 step -1
	
		// get the id of the bullet
		bulletID = bullet[bulletCounter].id

		if GetObjects3DPhysicsContactPositionVector(bulletID, minionID, vec3) = 1
			bits = bits + 123456
			bullet[bulletCounter].dead = 1
			cpuMinion[minionCounter].dead = 1
			INC MYHITS   /////////////////////////////DEBUG
		endif

	next bulletCounter
next minionCounter

endfunction
i also made it use WASD vs the Vstick.

i think i //'d any other changes...