monkey2 icon indicating copy to clipboard operation
monkey2 copied to clipboard

Problem with impulses on Scene startup

Open DruggedBunny opened this issue 7 years ago • 59 comments

Hi Mark,

Ran into weird problem with Thrust game, where I have to call Scene.Start followed up Scene.Update in order for impulses applied to my gems on startup to work. (They must be called prior to applying impulses, specifically.)

Without doing this, they simply don't appear on screen, though are still present/accessible in-source.

I think I've narrowed it down to this sample, although the same Start/Update fix doesn't work here, but it should be enough to show something's not quite right.

In Debug mode, this will work as expected -- spawn some cubes and apply a fixed impulse.

In Release mode, the cubes don't appear.

In Thrusting Game, after the above 'fix', they intentionally keep spinning at a fixed rate while playing, and I cancel out gravity so they hover.

If you comment out the call below to ApplyTorqueImpulse, or replace with a call to ApplyTorque (which doesn't have the same fixed-rate effect I'm after), they do then appear in Release as expected, just without the spin.

I'm not sure why my Start/Update fix works in Thrusting and not here, but it seems like doing this is necessary prior to applying any kind of impulse in Thrusting, in Release mode.

Debug mode 'fixes' Thrusting without needing the Start/Update thing.

(Something to do with how Scene and Entity.Start work, in conjunction with the instant timing needed for Impulse to apply??)

Anyway, this at least demos the "entity-missing-when-impulse-applied" thing, so hopefully if you can figure that out, the rest will fall into place...

Again, works as expected in Debug.


' Debug: Works

' Release: No cubes unless you comment out this line under ' START LOOP

' 		c.body.ApplyTorqueImpulse (New Vec3f (1.0, 1.0, 1.0))

' Only seems to apply to impulses (inc. ApplyImpulse)... ApplyTorque works as expected (but doesn't apply fixed spin immediately)...

' Also see comment in Update ()

#Import "<std>"
#Import "<mojo3d>"
 
Using std..
Using mojo..
Using mojo3d..

Class Cube

	Field coll:BoxCollider
	Field body:RigidBody

	Method New (loop:Int)

		Local box:Boxf = New Boxf (-0.5, -0.5, -0.5, 0.5, 0.5, 0.5)
		Local cube:Model = Model.CreateBox (box, 4, 4, 4, New PbrMaterial (Color.Red))

		cube.Move (loop * 2 - loop * 0.5, 0, 5)
		coll = cube.AddComponent <BoxCollider> ()
		body = cube.AddComponent <RigidBody> ()
		body.Mass = 1.0
		
	End
	
End

Class Game Extends Window

	Field scene:Scene
	Field camera:Camera
	Field light:Light
	
	Field cubelist:List <Cube>
	
	Method New (title:String, width:Int, height:Int, flags:WindowFlags)
		
		Super.New (title, width, height, flags)
		
		cubelist = New List <Cube>
		
		scene 	= Scene.GetCurrent ()
		
		camera	= New Camera
		light	= New Light
		
		camera.Move (0, 0, -5)
		
		' START LOOP
		For Local loop:Int = 1 To 10
		
			Local c:Cube = New Cube (loop)
			
			c.body.ApplyTorqueImpulse (New Vec3f (1.0, 1.0, 1.0)) ' Comment me out to work in Release!
			
			cubelist.AddLast (c)
			
		Next
		
	End
	
	Method Update ()
		
		If Keyboard.KeyHit (Key.Escape) Then App.Terminate ()
		
		' Comment out ApplyTorque line above and hit I just after running -- this works too...
		
		If Keyboard.KeyHit (Key.I)
			For Local c:Cube = Eachin cubelist
				c.body.ApplyTorqueImpulse (New Vec3f (1.0, 1.0, 1.0)) ' Comment me out to work in Release!
			Next
		Endif
		
	End
	
	Method OnRender (canvas:Canvas) Override
 
 		Update ()
 		
 		scene.Update ()
		scene.Render (canvas)
		
		RequestRender ()
		
	End
	
End
 
Function Main ()

	Local width:Int			= 640
	Local height:Int		= 480
	Local flags:WindowFlags	= WindowFlags.Center
	
	New AppInstance
	
	New Game ("", width, height, flags)
	
	App.Run ()
		
End

After testing that, try the thing mentioned in method Update (), as that shows it works once the scene is up and running, which may be another pointer in the right direction...

DruggedBunny avatar Jun 25 '18 22:06 DruggedBunny

Just looking at this now and I get the opposite - works in release but not in debug. Can you confirm?

On Tue, Jun 26, 2018 at 10:16 AM DruggedBunny [email protected] wrote:

Hi Mark,

Ran into weird problem with Thrust game, where I have to call Scene.Start followed up Scene.Update (or call Update twice -- as it calls Start) in order for impulses applied to my gems on startup to work.

Without doing this, they simply don't appear on screen, though are still present/accessible in-source.

I think I've narrowed it down to this sample, although the same Start/Update fix doesn't work here, but it should be enough to show something's not quite right.

In Debug mode, this will work as expected -- spawn some cubes and apply a fixed impulse. In Thrusting Game, after the fix, they keep spinning at a fixed rate while playing. (I cancel out gravity so they hover.)

In Release mode, the cubes don't appear.

However, if you comment out the call to ApplyTorqueImpulse, or replace with a call to ApplyTorque (which doesn't have the same fixed-rate effect), they do appear in Release as expected, just without the spin.

I'm not sure why my Start/Update fix works in Thrusting, but it seems like doing this is necessary prior to applying any kind of impulse.

(Something to do with how Scene and Entity.Start work, in conjunction with the instant timing needed for Impulse to apply??)

Anyway, this at least demos the "entity-missing-when-impulse-applied" thing, so hopefully if you can figure that out, the rest will fall into place...

Again, works as expected in Debug.

' Debug: Works

' Release: No cubes unless you comment out this line under ' START LOOP

' c.body.ApplyTorqueImpulse (New Vec3f (1.0, 1.0, 1.0))

' Only seems to apply to Impulse (inc. ApplyImpulse)... ApplyTorque works as expected (but doesn't apply fixed spin immediately)...

' Also see comment in Update ()

#Import "" #Import ""

Using std.. Using mojo.. Using mojo3d..

Class Cube

Field coll:BoxCollider Field body:RigidBody

Method New (loop:Int)

  Local box:Boxf = New Boxf (-0.5, -0.5, -0.5, 0.5, 0.5, 0.5)
  Local cube:Model = Model.CreateBox (box, 4, 4, 4, New PbrMaterial (Color.Red))

  cube.Move (loop * 2 - loop * 0.5, 0, 5)
  coll = cube.AddComponent <BoxCollider> ()
  body = cube.AddComponent <RigidBody> ()
  body.Mass = 1.0

End

End

Class Game Extends Window

Field scene:Scene Field camera:Camera Field light:Light

Field cubelist:List <Cube>

Method New (title:String, width:Int, height:Int, flags:WindowFlags)

  Super.New (title, width, height, flags)
  
  cubelist = New List <Cube>
  
  scene 	= Scene.GetCurrent ()
  
  camera	= New Camera
  light	= New Light
  
  camera.Move (0, 0, -5)
  
  ' START LOOP
  For Local loop:Int = 1 To 10
  
  	Local c:Cube = New Cube (loop)
  	
  	c.body.ApplyTorqueImpulse (New Vec3f (1.0, 1.0, 1.0)) ' Comment me out to work in Release!
  	
  	cubelist.AddLast (c)
  	
  Next

End

Method Update ()

  If Keyboard.KeyHit (Key.Escape) Then App.Terminate ()
  
  ' Comment out ApplyTorque line above and hit I just after running -- this works too...
  
  If Keyboard.KeyHit (Key.I)
  	For Local c:Cube = Eachin cubelist
  		c.body.ApplyTorqueImpulse (New Vec3f (1.0, 1.0, 1.0)) ' Comment me out to work in Release!
  	Next
  Endif

End

Method OnRender (canvas:Canvas) Override

  Update ()
  
  scene.Update ()
  scene.Render (canvas)
  
  RequestRender ()

End

End

Function Main ()

Local width:Int = 640 Local height:Int = 480 Local flags:WindowFlags = WindowFlags.Center

New AppInstance

New Game ("", width, height, flags)

App.Run ()

End

After testing that, try the thing mentioned in method Update (), as that shows it works once the scene is up and running, may be another pointer in the right direction...

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/blitz-research/monkey2/issues/399, or mute the thread https://github.com/notifications/unsubscribe-auth/ADU3QlA3c_Tg1sw5NSqVF_WLR0MT0P1dks5uAWE2gaJpZM4U28IX .

blitz-research avatar Jun 27 '18 00:06 blitz-research

Just checked -- nope, definitely working in Debug, and not in Release for me!

I'm building with MSVC and x64 here. I'll start other module formats building (x86 and MinGW) and see if any different...

Seems like some subtle timing problem, maybe something to do with how impulses take place instantly and might need a few scene updates to kick in? Only seems to affect impulses (ApplyTorqueImpulse, ApplyImpulse).

Suspect if you can fix your results to work in both, that will sort it!

Will change thread title back...

DruggedBunny avatar Jun 27 '18 00:06 DruggedBunny

Try it with this:

Thrusting

Edit imports/level.monkey2 -> New, just before End:

		...

		New Pad (93.0, 38.0, -365.0,, Color.Lime)
		SpaceGemAdded ()
		
		space_gems_total = SpaceGemCount

		' Scene.Update doesn't work unless called twice! Gems remain hidden...
		
		' Start () or a call to Scene.Update () -- which calls Start () -- works:
		
		Game.GameScene.Start ()
		
		Game.GameScene.Update ()			' Fixes missing physics bodies at startup;
											' Believe bodies need update BEFORE applying impulses!
											
		' Without Start (), another call to Scene.Update () is required...
'		Game.GameScene.Update ()

		SpaceGem.Start () ' Applies torque impulse to start spinning immediately

	End

Comment out Game.GameScene.Start () or Game.GameScene.Update () and see if the gems are missing in Debug or Release. Missing in Release for me.

Also, if you comment out .Start (), try adding the second commented-out .Update () line, which also fixes it here.

The weird thing for me is that it was working a few days back with only a single .Update prior to calling *Impulse () and I don't believe I changed anything before it went wrong! Must have done, but it seemed to come out of the blue, just didn't work one day...

DruggedBunny avatar Jun 27 '18 00:06 DruggedBunny

Ok, possible fix now up in develop branch.

On Wed, Jun 27, 2018 at 12:46 PM DruggedBunny [email protected] wrote:

Just checked -- nope, definitely working in Debug, and not in Release for me!

I'm building with MSVC and x64 here. I'll start other module formats building (x86 and MinGW) and see if any different...

Seems like some subtle timing problem, maybe something to do with how impulses take place instantly and might need a few scene updates to kick in? Only seems to affect impulses (ApplyTorqueImpulse, ApplyImpulse).

Suspect if you can fix your results to work in both, that will sort it!

Will change thread title back...

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/blitz-research/monkey2/issues/399#issuecomment-400507642, or mute the thread https://github.com/notifications/unsubscribe-auth/ADU3Qg7jY7pcil7mts5WC2YKAHwN0jumks5uAtX1gaJpZM4U28IX .

blitz-research avatar Jun 27 '18 00:06 blitz-research

OK, will go update...

DruggedBunny avatar Jun 27 '18 00:06 DruggedBunny

...and another last minute push!

On Wed, Jun 27, 2018 at 12:58 PM DruggedBunny [email protected] wrote:

OK, will go update...

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/blitz-research/monkey2/issues/399#issuecomment-400509340, or mute the thread https://github.com/notifications/unsubscribe-auth/ADU3Qtvs-3s3xvhr3XJSts_BFLe-EY_Bks5uAtizgaJpZM4U28IX .

blitz-research avatar Jun 27 '18 01:06 blitz-research

Yes! That looks to have done it -- above test works in both modes, Thrust works in both without Scene.Update/Start call and without my SpaceGem.Start hack (ie. I can just apply impulse when New'd).

My SmokeParticles have gone weird, but were probably just 'working' based off the broken behaviour, so will try sort that tomorrow.

Thanks, Mark, amazingly quick fix!

DruggedBunny avatar Jun 27 '18 02:06 DruggedBunny

Ah, smoke just needed impulse value decimated. Dunno how that was working before, must again have been accidentally working off the broken startup!

thrust = New Vec3f (0.0, -0.3 * multi, 0.0)

Been round and cleared the level without any problems!

DruggedBunny avatar Jun 27 '18 02:06 DruggedBunny

This one looks to be resolved, but sort of off the back of this, I became obsessed (while in and out of sleep) with the idea of having an OnSceneUpdate (or just OnUpdate) method attached to Entity.

It would be pretty darn tidy not to need a standalone list for any type of entity you want to revisit each update (eg. the gems, smoke particles), instead just having a smoke.OnUpdate callback (component?) where you could define what happens in OnUpdate in the class definition itself.

No need to find somewhere to stick in a SmokeParticle.List, a SpaceGemList, etc, and manually iterate through each one at each update.

Might it have performance implications, though?

DruggedBunny avatar Jun 27 '18 07:06 DruggedBunny

This is really what components are for, eg: you create a GemComponent, PlayerComponent, and use 'AddComponent' to attach them to models or pivots or whatever.

All the crap you already had in your 'BoxBody' etc. classes can go in here too. Components can 'see' other components by using 'Entity.RigidBody' (or Entity.GetComponent) etc so you can also drop many of the fields you had.

If you want to try this, best to probably extend Behaviour instead of Component to start with as this takes care of a bunch of stuff for you.

On Wed, Jun 27, 2018 at 7:22 PM DruggedBunny [email protected] wrote:

This one looks to be resolved, but sort of off the back of this, I became obsessed (while in and out of sleep) with the idea of having an OnSceneUpdate (or just OnUpdate) method attached to Entity.

It would be pretty darn tidy not to have to set up a standalone list for any type of entity you want to revisit each update (eg. the gems, smoke particles), instead just having a smoke.OnUpdate callback (component?) where you could update them in the class itself.

No need to find somewhere to stick in a SmokeParticle.List, a SpaceGemList, etc, and manually iterate through each one at each update.

Might it have performance implications, though?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/blitz-research/monkey2/issues/399#issuecomment-400569605, or mute the thread https://github.com/notifications/unsubscribe-auth/ADU3QnWl_EOI2wek0vGXH8Yuov61Xxmqks5uAzLLgaJpZM4U28IX .

blitz-research avatar Jun 27 '18 09:06 blitz-research

Hmm, OK, will look into that -- I'm not using those old BoxBody etc things any more (the ToyBox thing), as AddComponent really simplified setup of physics objects.

How would you link that in with Scene.Update, though, is there some sort of callback available already?

I was thinking of something like:

' Main loop

Method Render ()

	scene.Update () ' Calls OnSceneUpdate on all Entities
	
	' Etc

End

Class SmokeParticle

	Method OnSceneUpdate () ' Called by scene.Update
	
		ApplyForce
		ReduceAlpha
		If alpha < 0 Then Destroy ()
		
	End
	
End

Hence no need to manually manage lists of different entities, each on just gets called automatically and can carry out its own update, removal, etc.

Is that doable via Components?

DruggedBunny avatar Jun 27 '18 12:06 DruggedBunny

Er, I guess SmokeParticle isn't an Entity, duh, but could potentially extend Model there I imagine...

DruggedBunny avatar Jun 27 '18 12:06 DruggedBunny

Components have an OnUpdate method, have a look at FlyBehaviour in mojo3d for an example of writing a 'flyable' component. There are also methods for updating visiblity, and there will be more methods added in in future.

On Thu, Jun 28, 2018 at 12:40 AM DruggedBunny [email protected] wrote:

Er, I guess SmokeParticle isn't an Entity, duh, but could potentially extend Model there I imagine...

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/blitz-research/monkey2/issues/399#issuecomment-400657449, or mute the thread https://github.com/notifications/unsubscribe-auth/ADU3QlMjQEu_cOi4gQKQfFsUCk5d_pThks5uA31LgaJpZM4U28IX .

blitz-research avatar Jun 27 '18 21:06 blitz-research

Ah, sounds good, will give it a go.

DruggedBunny avatar Jun 28 '18 00:06 DruggedBunny

I did mess about with Components, and got something basic working, but calling component.OnUpdate passes in elapsed:Float, and over time my spacegems were slowly floating away, whereas they weren't obviously doing so when manually iterating through a list during OnRender (simply applying negative gravity), so I guess it comes down to when the update happens.

I wasn't able to fix this using component.OnUpdate's elapsed param (and suspect it would not be possible anyway due to minor timing errors), so thought I'd try giving them a Collider with no RigidBody, manually doing model.Rotate instead, which would be all I need.

However, as the entity.Collided () lambda passes in the other colliding RigidBody, I think this fails because I don't have a RigidBody attached to my entity -- I get no collision.

Should it be possible to detect pure collisions using only the Collider?

thrusting.zip

(All a bit of a mess just now, but see imports/spacegem.monkey2, line 43/44 -- expecting to see "Hi" printed on collision with gem... )

DruggedBunny avatar Jul 11 '18 11:07 DruggedBunny

Is there any way you can produce a one file version of the problem with Component.OnUpdate. The project's become kind of hard to navigate around...

In theory, if you're doing this:

Method OnRender() UpdateMyGame() 'my update logic... Scene.Update() Scene.Render() End

It should be basically the same as implementing Component.OnUpdate and just ignoring elapsed param (assuming UpdateMyGame ignores elapsed time too of course). Scene.Update() is what is (or should be) calling Component.OnUpdate, so they should roughly be the same thing.

Or is this not how your update code works?

On Wed, Jul 11, 2018 at 11:38 PM DruggedBunny [email protected] wrote:

I did mess about with Components, and got something basic working, but calling component.OnUpdate passes in elapsed:Float, and over time my spacegems were slowly floating away, whereas they weren't obviously doing so when manually iterating through a list during OnRender (simply applying negative gravity), so I guess it comes down to when the update happens.

I wasn't able to fix this using component.OnUpdate's elapsed param (and suspect it would not be possible anyway due to minor timing errors), so thought I'd try giving them a Collider with no RigidBody, manually doing model.Rotate instead, which would be all I need.

However, as the entity.Collided () lambda passes in the other colliding RigidBody, I think this fails because I don't have a RigidBody attached to my entity -- I get no collision.

Should it be possible to detect pure collisions using only the Collider?

thrusting.zip https://github.com/blitz-research/monkey2/files/2184226/thrusting.zip

(All a bit of a mess just now, but see imports/spacegem.monkey2, line 43/44 -- expecting to see "Hi" printed on collision with gem... )

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/blitz-research/monkey2/issues/399#issuecomment-404138685, or mute the thread https://github.com/notifications/unsubscribe-auth/ADU3QsXOTD_I54mB4DfNdEZsEn0xzDWhks5uFeOegaJpZM4U28IX .

blitz-research avatar Jul 11 '18 22:07 blitz-research

Ok, found OnRender and UpdateGame but I can't see where you process the gems list.

But it looks like your basic update logic should work with Component.OnUpdate. If you were to make GameMenu, Player, HUD and perhaps even CurrentLevel all subclasses of Behaviour, they could all be automatically OnUpdated for you inside Scene.OnUpdate. This also makes them very reusable and quite easy to serialize.

As for colliders, bullet seems to need the rigidbody for collisionmask/collisiongroup, but I'll look into it again soon-ish.

On Thu, Jul 12, 2018 at 10:32 AM Mark Sibly [email protected] wrote:

Is there any way you can produce a one file version of the problem with Component.OnUpdate. The project's become kind of hard to navigate around...

In theory, if you're doing this:

Method OnRender() UpdateMyGame() 'my update logic... Scene.Update() Scene.Render() End

It should be basically the same as implementing Component.OnUpdate and just ignoring elapsed param (assuming UpdateMyGame ignores elapsed time too of course). Scene.Update() is what is (or should be) calling Component.OnUpdate, so they should roughly be the same thing.

Or is this not how your update code works?

On Wed, Jul 11, 2018 at 11:38 PM DruggedBunny [email protected] wrote:

I did mess about with Components, and got something basic working, but calling component.OnUpdate passes in elapsed:Float, and over time my spacegems were slowly floating away, whereas they weren't obviously doing so when manually iterating through a list during OnRender (simply applying negative gravity), so I guess it comes down to when the update happens.

I wasn't able to fix this using component.OnUpdate's elapsed param (and suspect it would not be possible anyway due to minor timing errors), so thought I'd try giving them a Collider with no RigidBody, manually doing model.Rotate instead, which would be all I need.

However, as the entity.Collided () lambda passes in the other colliding RigidBody, I think this fails because I don't have a RigidBody attached to my entity -- I get no collision.

Should it be possible to detect pure collisions using only the Collider?

thrusting.zip https://github.com/blitz-research/monkey2/files/2184226/thrusting.zip

(All a bit of a mess just now, but see imports/spacegem.monkey2, line 43/44 -- expecting to see "Hi" printed on collision with gem... )

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/blitz-research/monkey2/issues/399#issuecomment-404138685, or mute the thread https://github.com/notifications/unsubscribe-auth/ADU3QsXOTD_I54mB4DfNdEZsEn0xzDWhks5uFeOegaJpZM4U28IX .

blitz-research avatar Jul 11 '18 22:07 blitz-research

Another thing to try is overriding Component.OnBeginUpdate() instead. OnUpdate() is actually called after physics is updated, whereas OnBeginUpdate() is called before so is more similar to your update logic.

On Thu, Jul 12, 2018 at 10:43 AM Mark Sibly [email protected] wrote:

Ok, found OnRender and UpdateGame but I can't see where you process the gems list.

But it looks like your basic update logic should work with Component.OnUpdate. If you were to make GameMenu, Player, HUD and perhaps even CurrentLevel all subclasses of Behaviour, they could all be automatically OnUpdated for you inside Scene.OnUpdate. This also makes them very reusable and quite easy to serialize.

As for colliders, bullet seems to need the rigidbody for collisionmask/collisiongroup, but I'll look into it again soon-ish.

On Thu, Jul 12, 2018 at 10:32 AM Mark Sibly [email protected] wrote:

Is there any way you can produce a one file version of the problem with Component.OnUpdate. The project's become kind of hard to navigate around...

In theory, if you're doing this:

Method OnRender() UpdateMyGame() 'my update logic... Scene.Update() Scene.Render() End

It should be basically the same as implementing Component.OnUpdate and just ignoring elapsed param (assuming UpdateMyGame ignores elapsed time too of course). Scene.Update() is what is (or should be) calling Component.OnUpdate, so they should roughly be the same thing.

Or is this not how your update code works?

On Wed, Jul 11, 2018 at 11:38 PM DruggedBunny [email protected] wrote:

I did mess about with Components, and got something basic working, but calling component.OnUpdate passes in elapsed:Float, and over time my spacegems were slowly floating away, whereas they weren't obviously doing so when manually iterating through a list during OnRender (simply applying negative gravity), so I guess it comes down to when the update happens.

I wasn't able to fix this using component.OnUpdate's elapsed param (and suspect it would not be possible anyway due to minor timing errors), so thought I'd try giving them a Collider with no RigidBody, manually doing model.Rotate instead, which would be all I need.

However, as the entity.Collided () lambda passes in the other colliding RigidBody, I think this fails because I don't have a RigidBody attached to my entity -- I get no collision.

Should it be possible to detect pure collisions using only the Collider?

thrusting.zip https://github.com/blitz-research/monkey2/files/2184226/thrusting.zip

(All a bit of a mess just now, but see imports/spacegem.monkey2, line 43/44 -- expecting to see "Hi" printed on collision with gem... )

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/blitz-research/monkey2/issues/399#issuecomment-404138685, or mute the thread https://github.com/notifications/unsubscribe-auth/ADU3QsXOTD_I54mB4DfNdEZsEn0xzDWhks5uFeOegaJpZM4U28IX .

blitz-research avatar Jul 12 '18 01:07 blitz-research

Thanks for having a look, Mark, will look into what you've said here, since you seem confident it should work! Yes, bit tricky to follow, no doubt... I'll narrow it down if/when I get stuck again...

Regarding the missing gem list, I was using the Component thing (commented-out under spacegem.monkey2 as Class SpaceGemComponent) to manage them, instead of using a list, ie. create the gems, then use component.OnUpdate to visit each one -- that worked really well, but I was getting the floating-away effect.

I actually found plain Component easier to grasp than Behaviour (after eventually narrowing down the minimum I needed), but will have another look at Behaviour too. This might have been because I really just wanted the OnUpdate functionality to skip maintaining a separate list.

For what it's worth, I ended up with this generic version, attaching the SpaceGem object under ao:MyClass so I could access its members from the Entity/Component update:

Class CustomComponent Extends Component

	Private
	
		Const COMPONENT_PRIORITY:Int = 0
		Const COMPONENT_TYPE:ComponentType = New ComponentType ("CustomComponent", COMPONENT_PRIORITY, ComponentTypeFlags.Singleton)
		
		Field ao:MyClass ' Attached object (modify/delete to suit) -- see AttachedOject property
		
		Method OnUpdate (elapsed:Float) Override
		
			' eg.
			
			Entity.GetComponent <RigidBody> ()?.ApplyForce (Game.GameScene.World.Gravity * New Vec3f (1.0, -1.0, 1.0))
			
			AttachedObject?.DoSomething ()

		End
		
	Public
	
		Method New (entity:Entity)
			Super.New (entity, COMPONENT_TYPE)
			AddInstance ()
		End
	
		Property AttachedObject:MyClass () ' Modify/delete to suit
			Return ao
			Setter (new_ao:MyClass)
				ao = new_ao
		End
		
End

Probably missing something, though... was just an experiment in ditching management of manual lists!

Realistically these gems don't need physics, so I can always go back to simple models with distance checks if all else fails, just seems more consistent and feels more accurate when collecting...

Thanks again, you've obviously spent a fair bit of time looking at this. I'll dig into FlyBehaviour again... at least it sounds like this 'should' work, and I can at least produce a stripped-down version if not...

DruggedBunny avatar Jul 12 '18 03:07 DruggedBunny

Looks like FlyBehaviour is very similar to the above, just takes care of the ComponentType/priority stuff, so I'll use Behaviour instead! Going to start SpaceGem from scratch and see how it goes...

EDIT: Ah, I now see how you stick the stuff that would otherwise be my SpaceGem object into the Behaviour instead...

DruggedBunny avatar Jul 12 '18 12:07 DruggedBunny

Hi Mark,

You might find this of interest -- this example shows that applying force (negative gravity here) to a body at different update points gives slightly different, but significant, results. OnBeginUpdate seems to give the result I wanted, though, so going to try plugging that into SpaceGem...

#Import "<std>"
#Import "<mojo3d>"
 
Using std..
Using mojo..
Using mojo3d..

Const APPLY_IN_ONRENDER:Int	= 1		' Main loop				' Floats upwards
Const APPLY_IN_ONUPDATE:Int	= 2		' Behaviour.OnUpdate		' Floats downwards
Const APPLY_IN_ONBEGIN:Int		= 3		' Behaviour.OnBeginUpdate	' Rock solid!

' Apply gravity at this point:

Const APPLY_WHEN:Int = APPLY_IN_ONRENDER


' Note to self:

'			body.btBody.setGravity (New bullet.btVector3 (0.0, 0.0, 0.0))
'			^ this called *every update* slowly settles to no movement


Class SpaceGemBehaviour Extends Behaviour
	
	Public

		Method New (entity:Entity)
			Super.New (entity)
			model = Cast <Model> (entity)
			AddInstance ()
		End
		
		Method AddBody (box:Boxf)

			body = New RigidBody (model)
			body.Mass = 1.0
			
			collider = New BoxCollider (model)
			collider.Box = box

			' Spin me!
			
			'body.ApplyTorqueImpulse (New Vec3f (0.1, 0.2, 0.4))

		End
		
		Method OnBeginUpdate () Override
			If APPLY_WHEN = APPLY_IN_ONBEGIN
				body.ApplyForce (Game.GameScene.World.Gravity * New Vec3f (1.0, -1.0, 1.0))
			Endif
		End
		
		Method OnUpdate (elapsed:Float) Override
			If APPLY_WHEN = APPLY_IN_ONUPDATE
				body.ApplyForce (Game.GameScene.World.Gravity * New Vec3f (1.0, -1.0, 1.0))
			Endif
		End

'	Private

		Field model:Model
		Field body:RigidBody			' Bullet physics body
		Field collider:BoxCollider		' Bullet physics collider
	
End

Class Game Extends Window
	
	' Basic 3D scene requirements...
	
	Global GameScene:Scene
	
	Field camera:Camera
	Field light:Light
	
	Field gem:Model
	Field gembehaviour:SpaceGemBehaviour
	
	Method New (title:String, width:Int, height:Int, flags:WindowFlags)
		
		Super.New (title, width, height, flags)
		
		GameScene		= Scene.GetCurrent () ' Important!
		
		camera			= New Camera
 
		camera.Move (0, 0, -5)
		
		light			= New Light
		
		Local box:Boxf	= New Boxf (-0.5, -0.5, -0.5, 0.5, 0.5, 0.5)
		
		gem				= Model.CreateBox (box, 2, 2, 2, New PbrMaterial (Color.Red))
		
		gembehaviour	= gem.AddComponent <SpaceGemBehaviour> ()
		gembehaviour.AddBody (box)
		
	End
	
	Method OnRender (canvas:Canvas) Override

		If Keyboard.KeyHit (Key.Escape) Then App.Terminate ()
		
		If APPLY_WHEN = APPLY_IN_ONRENDER
			gembehaviour.body.ApplyForce (Game.GameScene.World.Gravity * New Vec3f (1.0, -1.0, 1.0))
		Endif
		
		GameScene.Update ()
		GameScene.Render (canvas)
		
		RequestRender ()
		
	End
	
End

Function Main ()
 
	' Windowed mode...
	
	Local width:Int			= 640
	Local height:Int		= 480
	Local flags:WindowFlags	= WindowFlags.Center
	
	New AppInstance
	
	New Game ("", width, height, flags)
	
	App.Run ()
		
End

I think my model and body fields are probably redundant, as I think I can access Entity and (from there) the attached RigidBody, so will try strip this down a bit...

Anyway, it shows the effect of applying the force at different times!

DruggedBunny avatar Jul 15 '18 15:07 DruggedBunny

OnBeginUpdate has solved my SpaceGems thing, anyway... think I now just need to spawn a final Orb to collect and fly through a suspiciously CreateTorus-shaped SpacePortal to finish it off! (OK, bit more tweaking, tidying up, etc too... )

DruggedBunny avatar Jul 15 '18 20:07 DruggedBunny

Just ran into an interesting Debug vs Release quirk with the above code, with OnUpdate replaced by this version (adds hit-Space-to-delete-model)...

		Method OnUpdate (elapsed:Float) Override
			If APPLY_WHEN = APPLY_IN_ONUPDATE
				body.ApplyForce (Game.GameScene.World.Gravity * New Vec3f (1.0, -1.0, 1.0))
			Endif
			If Keyboard.KeyHit (Key.Space)
				model.Destroy ()
			Endif
		End

It works in Release mode, but causes Invalid stack iterator in Debug mode.

I imagine this shouldn't actually work anyway (deleting the entity the behaviour is attached to, from within the behaviour).

You can't pass any data in on AddComponent or New Behaviour, so other than bundling data into fields within the Behaviour, I'm not sure how to do this... and can't then delete the entity from OnUpdate when it's no longer needed.

My understanding is that Behaviour should just be adding actions to Entity, but in the example above, I need to access the RigidBody, so have to sort of hack it in after adding the Behaviour... (see AddBody).

Like I say, this nasty hackery seems to work in Release here, but not in Debug... model.Destroy () causes the exception.

Is there a better way to wrap up non-Entity stuff with behaviours (eg. RigidBody and other object data) than what I'm attempting here?

DruggedBunny avatar Jul 17 '18 23:07 DruggedBunny

Best workaround I could come up with for deleting entities from within Behaviour.OnUpdate:

[EDIT: Seems to work well, having updated game to use this.]

#Import "<std>"
#Import "<mojo3d>"
 
Using std..
Using mojo..
Using mojo3d..

#Rem

	Method:
	
	1) Use MarkEntity (entity) to mark for deletion in Behaviour.OnUpdate () (add to list)
	2) Use RemoveMarkedEntities () to remove marked entities in Game.OnRender (after scene.Update ())
	
	Game.MarkEntity (entity:Entity)
	Game.RemoveMarkedEntities ()

#End

' Note to self:

'			body.btBody.setGravity (New bullet.btVector3 (0.0, 0.0, 0.0))
'			^ this called *every update* slowly settles to no movement

Class SpaceGemBehaviour Extends Behaviour
	
	Public

		Method New (entity:Entity)
			Super.New (entity)
			model = Cast <Model> (entity)
			AddInstance ()
		End
		
		Method AddBody (box:Boxf)

			body = New RigidBody (model)
			body.Mass = 1.0
			
			collider = New BoxCollider (model)
			collider.Box = box

			' Spin me!
			
			body.ApplyTorqueImpulse (New Vec3f (0.1, 0.2, 0.4))

		End
		
		Method OnBeginUpdate () Override
			body.ApplyForce (Game.GameScene.World.Gravity * New Vec3f (1.0, -1.0, 1.0))
		End
		
		Method OnUpdate (elapsed:Float) Override
		
			If Keyboard.KeyHit (Key.Space)
			
				Game.MarkEntity (Entity) ' Mark for deletion in main loop (RemoveMarkedEntities)...
				
				Destroy () ' Sets local entity ref to Null but doesn't remove model from scene AFAICT

				'body.Destroy () ' Crash
				
				body = Null
				
			Endif
			
		End
		
'	Private

		Field model:Model
		Field body:RigidBody			' Bullet physics body
		Field collider:BoxCollider		' Bullet physics collider
	
End

Class Game Extends Window
	
	' Basic 3D scene requirements...
	
	Global GameScene:Scene
	Global MarkedEntities:List <Entity>
	
	Field camera:Camera
	Field light:Light
	
	Field gem:Model
	Field gembehaviour:SpaceGemBehaviour
	
	Function MarkEntity (entity:Entity)
		Game.MarkedEntities.AddLast (entity)
	End
	
	Function RemoveMarkedEntities ()
		For Local entity:Entity = Eachin Game.MarkedEntities
			entity?.Destroy ()
			Game.MarkedEntities.Remove (entity)
		Next
	End
	
	Method New (title:String, width:Int, height:Int, flags:WindowFlags)
		
		Super.New (title, width, height, flags)
		
		GameScene		= Scene.GetCurrent () ' Important!
		MarkedEntities	= New List <Entity>
		
		camera			= New Camera
 
		camera.Move (0, 0, -5)
		
		light			= New Light
		
		Local box:Boxf	= New Boxf (-0.5, -0.5, -0.5, 0.5, 0.5, 0.5)
		
		gem				= Model.CreateBox (box, 2, 2, 2, New PbrMaterial (Color.Red))
		
		gembehaviour	= gem.AddComponent <SpaceGemBehaviour> ()
		gembehaviour.AddBody (box)
		
	End
	
	Method OnRender (canvas:Canvas) Override

		If Keyboard.KeyHit (Key.Escape) Then App.Terminate ()
		
		GameScene.Update ()
		
		RemoveMarkedEntities ()
		
		GameScene.Render (canvas)
		
		RequestRender ()
		
	End
	
End

Function Main ()
 
	' Windowed mode...
	
	Local width:Int			= 640
	Local height:Int		= 480
	Local flags:WindowFlags	= WindowFlags.Center
	
	New AppInstance
	
	New Game ("", width, height, flags)
	
	App.Run ()
		
End

DruggedBunny avatar Jul 21 '18 13:07 DruggedBunny

Yes, Destroy with entites/components will probably need to be delayed until 'end of update' when executed inside of update. Outside of update it can occur immediately.

I might also pinch unity's idea of 'Destroy( delay:Float=0 )' which allows you to destroy something after some time has elapsed.

Will get onto this stuff for next release!

Also, have you check out the monkey2 discord channel? That seems to be the place to go for monkey2 chat these days.

On Wed, Jul 18, 2018 at 11:18 AM DruggedBunny [email protected] wrote:

Just ran into an interesting Debug vs Release quirk with the above code, with OnUpdate replaced by this version (adds hit-Space-to-delete-model)...

  Method OnUpdate (elapsed:Float) Override
  	If APPLY_WHEN = APPLY_IN_ONUPDATE
  		body.ApplyForce (Game.GameScene.World.Gravity * New Vec3f (1.0, -1.0, 1.0))
  	Endif
  	If Keyboard.KeyHit (Key.Space)
  		model.Destroy ()
  	Endif
  End

It works in Release mode, but causes Invalid stack iterator in Debug mode.

I imagine this shouldn't actually work anyway (deleting the entity the behaviour is attached to, from within the behaviour), but is there a better way to wrap up non-Entity stuff with behaviours?

You can't pass any data in on AddComponent or New Behaviour, so other than bundling data into fields within the Behaviour, I'm not sure how to do this... and can't then delete the entity from OnUpdate when it's no longer needed.

My understanding is that Behaviour should just be adding actions to Entity, but in the example above, I need to access the RigidBody, so have to sort of hack it in after adding the Behaviour... (see AddBody).

Like I say, this nasty hackery seems to work in Release here, but not in Debug... model.Destroy () causes the exception.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/blitz-research/monkey2/issues/399#issuecomment-405758321, or mute the thread https://github.com/notifications/unsubscribe-auth/ADU3QkwhPlnPgNUknYlkvTueZrjGtQKNks5uHnDRgaJpZM4U28IX .

blitz-research avatar Jul 22 '18 00:07 blitz-research

Hi Mark,

When you get back to this, these are the points from this thread I'd highlight (realise it's got a bit sprawling/wordy on my part!)...

Detect pure collisions (no RigidBodies)

Difference between updating physics at different points (main loop, component.OnUpdate, component.OnBeginUpdate)

Debug vs Release mode difference (crash) with component.Destroy

Marking/Destroying entities from within component.OnUpdate

On the last point, this seems to work really well in my game -- turns out that, almost all of the time, the list of marked entities holds exactly 1 entity, due to the timing of New and the fixed rate at which the objects 'decay' (eg. fade out).

If I try New'ing 4 SmokeParticles instead of 1, the list holds 4 particles pretty much all the time. Seems fairly predictable/low impact.

Destroy () could potentially include the marking portion so this doesn't appear as a separate step.

DruggedBunny avatar Jul 22 '18 10:07 DruggedBunny

Oh, yeah, Discord -- not tried it as I'm not into the whole realtime chat thing, always felt awkward about it (same with IRC, Facebook messages, etc), preferring to operate at my own speed, ie. come back to things later, but might have a look...

DruggedBunny avatar Jul 22 '18 10:07 DruggedBunny

Er, see highlights above!

DruggedBunny avatar Jul 22 '18 10:07 DruggedBunny

Just tooling around with this now and would like permission to clean up a few things:

  • Change Entity.Collided to RigidBody.Collided. This is where it should be IMO, as there's no reason for Entity to have to 'know' about rigidbodies like this.

  • Remove OnBeginUpdate() and change OnUpdate() to execute before physics update - we can add an OnEndUpdate or OnAfterUpdate later if need be, but IMO OnUpdate should be before physics update and I goofed here.

And whadya mean you're no good at facebook! Yor're way better than me, I can't even 'face' it these days!

On Sun, Jul 22, 2018 at 10:47 PM DruggedBunny [email protected] wrote:

Er, see highlights above!

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/blitz-research/monkey2/issues/399#issuecomment-406856873, or mute the thread https://github.com/notifications/unsubscribe-auth/ADU3QngZME_j9jHX0DM_9pHDGi16ZEuIks5uJFg6gaJpZM4U28IX .

blitz-research avatar Jul 25 '18 06:07 blitz-research

Ha, I hate Facebook, just can't get away now.

Yeah, no problem for me to update these things... actually got a fair bit done to the game (bit more controllable too), but these changes won't cause me any more than a couple of minutes' work (he says).

DruggedBunny avatar Jul 25 '18 07:07 DruggedBunny