Jump to content

Problem with collision callback


Uberman
 Share

Recommended Posts

I have the following simple (LEO-based) LE 2.5 program that compiles and functions properly under both VS2008 and VS2010:

 

//  ====================================================================
//  This file was generated by LEBuilder
//  http://leadwerks.com/werkspace
//  ====================================================================

#include "leo.h"
#include <string>
#include <iostream>

using namespace std;
using namespace LEO;

const int   ScreenWidth = 800;
const int   ScreenHeight = 600;
const char* MediaDir = "D:\\Leadwerks\\SDK";
const char* AppTitle = "TestLEO";

void ErrOut( const string& message ) { cerr << message << endl; }

//  --------------------------------------------
int main( int argc, char* argv[] )
{
// Set graphics mode
Engine engine(AppTitle,ScreenWidth,ScreenHeight);
if( !engine.IsValid() )
{
	ErrOut( "Failed to set graphics mode.");
	return 1;
}
engine.AddAbstractPath( MediaDir );

// Create framework object and set it to a global object so other scripts can access it
Framework fw;
if( NULL == fw )
{
	ErrOut( "Failed to initialize engine." );
	return 1;
}

// Set Lua framework object
engine.SetObject( "fw", fw );

// Set Lua framework variable
Lua lua;
lua.PushObject( fw );
lua.SetGlobal( "fw" );
lua.Pop( 1 );

// Get framework main camera
fw.main.GetCamera().SetPosition( Vec3(0,0,-5) );

Material material( "abstract::cobblestones.mat" );

Debug::SetDebugPhysics();

// Create collision cube
BodyBox cube1_body(1);
cube1_body.SetMass(1.0);
cube1_body.SetFriction(1.0, 1.0);
cube1_body.SetElasticity(0.2f);

Cube cube1;
cube1.Paint( material );
cube1.SetParent(cube1_body);

cube1_body.SetPosition(Vec3(0, 5, 0));

BodyBox cube2_body(1);
cube2_body.SetMass(1.0);
cube2_body.SetFriction(1.0, 1.0);
cube2_body.SetElasticity(0.2f);

Cube cube2;
cube2.Paint( material );
cube2.SetParent(cube2_body);

cube2_body.SetPosition(Vec3(0.5, 10, 0.5));

// Create collision ground
BodyBox ground_body(1);

Cube ground;
ground.Paint( material );
ground.SetParent(ground_body);

ground_body.SetScale(Vec3(10.0f, 0.1f, 10.0f));
ground_body.SetPosition(Vec3(0, -1, 0));

// Turn on physics
//TVec3 gravity = Vec3(0,-9.80665f*2,0);
TVec3 gravity = Vec3(0,-2.0,0);

World& back_world = fw.background.GetWorld();
back_world.SetCollisions(1, 1);
back_world.SetGravity(gravity);

World& main_world = fw.main.GetWorld();
main_world.SetCollisions(1, 1);
main_world.SetGravity(gravity);

World& trans_world = fw.transparency.GetWorld();
trans_world.SetCollisions(1, 1);
trans_world.SetGravity(gravity);

cube1_body.SetType(1);
cube2_body.SetType(1);
ground_body.SetType(1);

// Add some light
DirectionalLight light;
light.SetRotation( Vec3(45) );

// Spin cube until user hits Escape
while( !Keyboard::I****() && !engine.IsTerminated() )
{
	if( !engine.IsSuspended() )
	{
		fw.Update();
		fw.Render();

		engine.Flip( 0 );
	}
}

return engine.Free();
}

 

This works as I expect, with both cubes falling and colliding properly.

 

I now would like to detect a collision for each Cube body (to emit a sound, for example). I add the following module-level function to the code:

 

int cubeCollision(TEntity e1, TEntity e2)
{
 return 0;
}

 

And then I enable collision callbacks for each Cube toward the bottom of the main() body:

 

cube1_body.SetType(1);
cube1_body.SetCallback((BP)cubeCollision, ENTITYCALLBACK_COLLISION);
cube2_body.SetType(1);
cube2_body.SetCallback((BP)cubeCollision, ENTITYCALLBACK_COLLISION);
ground_body.SetType(1);

 

When the first impact occurs, and the callback is triggered, I'm getting a run-time error (the same in both VS2008 and VS2010) that states:

 

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

 

Any ideas about what I might be doing wrong here?

Link to comment
Share on other sites

I found this in leobase.h:

 

typedef void (*CollisionCallback)(TEntity ent0, TEntity ent1, const TVec3& pos, const TVec3& normal, flt speed);

 

But setting the callback to this signature still generates the runtime issue with VS2008 and VS2010. sad.png

Link to comment
Share on other sites

I had no problems with it so far, but maybe I added also a _stdcall to my callback function. I also use Code::Blocks, and Visual Studio 2012 to make the Microsoft fanatics happy :D

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Link to comment
Share on other sites

I have added both __stdcall and __cdecl to the callback function, and no joy.

 

I've also tried the straight C++ code (non-LEO) from the aforementioned tutorial in a fresh VS2008-based project generated by LEBuilder, added the callback function:

 

void __stdcall cubeCollision(TEntity e1, TEntity e2, const TVec3& pos, const TVec3& normal, flt speed) {}

 

or:

 

void __cdecl cubeCollision(TEntity e1, TEntity e2, const TVec3& pos, const TVec3& normal, flt speed) {}

 

set the callback:

 

SetEntityCallback(body, (BP)cubeCollision, ENTITYCALLBACK_COLLISION);

 

And I still get the same runtime error with VS2008 and VS2010.

 

I'm beginning to wonder if there's some compiler setting (like structure alignment) that the LEBuilder program is not adding when it generates the Visual Studio projects.

Link to comment
Share on other sites

I've just been playing around with this. I had pretty much the same problem with the same error.

 

The callback must be outside of a class I've noticed. I am by no means a master at this, however i have it working with collecting coins in my game. Some of the examples around are a little off, possibly out dated, i dont know.

just adjust the names to suit.

 

Here goes...

 

.cpp file

//Callbacks
int _stdcall CoinCollision( TEntity entity0, TEntity entity1, byte* position, byte* normal, byte* force, flt speed )
{
Inventory::CoinItem *item = reinterpret_cast<Inventory::Coinitem*>(GetEntityUserData(entity0));
EntityType(entity0,5);
TSound sound = LoadSound("Sound/Item/coinPickup.wav");
EmitSound(entity0, sound, 10.0F, 1.0F, SOURCE_EAX ) ;

Controller *controller = reinterpret_cast<controller*>(GetEntityUserData(entity1));
//etc etc....

return 0;
}

 

 

.h file

 

int _stdcall CoinCollision(TEntity entity0, TEntity entity1, byte* position, byte* normal, byte* force, flt speed );

 

 

Call from a function......

 

SetEntityCallback(yourEntity ,(byte*)CoinCollision,ENTITYCALLBACK_COLLISION);

trindieprod.png?dl=0spacer.png?dl=0steam-icon.png?dl=0twitter-icon.png?dl=0spacer.png?dl=0
Link to comment
Share on other sites

What I often do with this is make 1 collision function. All my classes derive from a base class that has a Virtual collision function. Inside each child class the entities that need to worry about collision I call SetEntityUserData() and pass 'this' to the entity data. Then inside my one collision function I call GetEntityUserData() casting to my base class. I then call the virtual collision function.

 

By doing this the one LE collision function serves to just pass control on collisions to the object that should handle them.

 

This way for all my games I do I can just copy and paste this function to my project and as long as I derive all my classes from the common base class, everything just works without having to worry about how to implement this callback. It would be pretty cool if LEO could do something like this automatically but LEO will most likely be dead with LE3D since that's basically what LE3D is now :)

  • Upvote 1
Link to comment
Share on other sites

Here goes...

 

Huh?! Your collision callback signature is even different from the prototype in the headers! blink.png

 

Yours:

 

int _stdcall CoinCollision( TEntity entity0, TEntity entity1, byte* position, byte* normal, byte* force, flt speed )

 

LEO/leobase.h Header:

 

typedef void (*CollisionCallback)(TEntity ent0, TEntity ent1, const TVec3& pos, const TVec3& normal, flt speed);

 

The LEO version in leobase.h is 2.5.0, but the C headers are 2.5.1. I'm beginning to get the feeling that the LEO headers are out of date with the current version of the library. The LEO header prototype does not define the "force" parameter found in your callback, but then the C headers don't even define a typedef for the callback prototype at all, so I can't compare.

Link to comment
Share on other sites

I've just been playing around with this. I had pretty much the same problem with the same error.

 

Your function prototype worked, tj. I don't get the runtime error now (because the correct number of arguments are being processed). Thank you for the concrete example. It let me see that there are definitely some disconnects in the SDK (and the documentation) for which I'll have to be prepared.

Link to comment
Share on other sites

Hi Rick,

 

Thanks for that. That would be a good way handling the collision callbacks. As I'm just starting out it's good to know these things early.

 

btw there were a few errors in my code, the editor for some reason some tags were add in when I saved, which i've deleted now.

Cheers!

trindieprod.png?dl=0spacer.png?dl=0steam-icon.png?dl=0twitter-icon.png?dl=0spacer.png?dl=0
Link to comment
Share on other sites

  • 8 months later...

i know this is rather old, but rick can i see a quick example snippet. I think it would help everyone here to see this as an example.

bool Life()
{
 while(death=false)
 {
   if(death==true)
   return death;
 }
}

 

I have found the secret to infinite life

 

Did I help you out? Like my post!

Link to comment
Share on other sites

I'll throw one off the top of my head here. Below should get you started and show the general idea. Let me know if you have any questions. This is a nice way to keep things object oriented.

 

class Object
{
private:
  // you can have a TEntity/TMesh/TModel here also since most every game "object" has a visual representation. then in this ctor you can set entity user data so that you never have to remember to do this
public:
  virtual void OnCollision(Object* obj, byte* position, byte* normal, byte* force, flt speed) {}
};

class Coin : public Object
{
private:
  TModel _coinModel;
public:
 Coin()
 {
     _coinModel = LoadModel("coin.gmf");

     // we do this so that when _coinModel entity is passed to the collision function we can call GetEntityUserData() and cast to Object
     SetEntityUserData(_coinModel, (byte*)this);

     // register this model to call the one collision method. every model in your game should register to this one function as long as it derives from Object
     SetEntityCallback(_coinModel, (byte*)Collision, ENTITYCALLBACK_COLLISION);
 }

 // override the collision function to handle collisions with the coin
 virtual void OnCollision(Object* obj, byte* position, byte* normal, byte* force, flt speed)
 {
     if(obj->IsPlayer())
     {
        Player* p = (Player)obj;
        p->GiveCoin(this);

        HideEntity(_coinModel);
     }
 }
};


// this is the one collision function for the entire game
int _stdcall Collision(TEntity entity0, TEntity entity1, byte* position, byte* normal, byte* force, flt speed)
{
  Object* obj1 = (Object*)GetEntityUserData(entity0);
  Object* obj2 = (Object*)GetEntityUserData(entity1);

  // if the objects are valid fire the collision between them. since this method is virtual it'll go to whatever object it is to handle things correctly
  if(obj1 && obj2)
    obj1->OnCollision(obj2, position, normal, force, speed);
}

Link to comment
Share on other sites

so what is the difference between the virtual polymorphism and just having each model in a class, each with it's own collision function. I don't see the need to use polymorphism here.

 

I also get this error


Error 1 error C2440: 'type cast' : cannot convert from 'void (__thiscall Stone::* )(coll *,byte *,byte *,byte *,LE::flt)' to 'byte *' c:\Documents and Settings\Chris\My Documents\Visual Studio 2008\Projects\Castle Defense\Castle Defense\Stone.cpp 40

//the line the error occurs in is here
SetEntityCallback(this->Full, (byte*)OnColl, ENTITYCALLBACK_COLLISION);
//this->Full is a TModel, OnColl is my collision function name

bool Life()
{
 while(death=false)
 {
   if(death==true)
   return death;
 }
}

 

I have found the secret to infinite life

 

Did I help you out? Like my post!

Link to comment
Share on other sites

so what is the difference between the virtual polymorphism and just having each model in a class, each with it's own collision function.

 

Nothing is wrong with it. That will work. I just try to stick to object oriented style of programming and normal C callbacks don't follow that pattern very well. That and I find this method to be easier to move around from game to game. It's the same 1 C callback between all my games, and the real different logic is put into the custom objects that game has via the collision method. It makes it so I don't have to worry about the clue code details and more just focus on the collision logic.

 

Generally what I do is store a TEntity (which can be a model) in my base class, since pretty much all game objects have a visual, and it automatically registers to the 1 C collision callback function and set's the user data. So all I have to do is derive from this base class and it's all done for me for every object. I don't have to think about it from game to game.

 

 

Can you show me the class setup where you have the SetEntityCallback() function call you posted? You can take out any parts that aren't relevant.

Link to comment
Share on other sites

i'm not quite sure what your asking for so i'll post everything regarding the collisions

 

//collisions class
//header file

#ifndef _M_COLL_H__
#define _M_COLL_H__

#include "engine.h"

class coll
{
public:
virtual void OnColl(coll *obj, byte* position, byte* normal, byte* force, flt speed) ;
};

#endif

//cpp file

#include "Collisions.h"

void coll::OnColl(coll *obj, byte* position, byte* normal, byte* force, flt speed)
{

}

//Stone class
//header file

#ifndef _M_STONE__H_
#define _M_STONE__H_

#include "engine.h"
#include "Collisions.h"

class Stone : public coll
{
private:
TModel Full;


public:
Stone();//constructor
~Stone();//destructor

virtual void OnColl(coll* ColObj, byte* position, byte* normal, byte* force, flt speed);
};

#endif //#ifndef _M_STONE__H_

//CPP file

Stone::Stone()
{
this->Full=LoadModel("stone//stone_1.gmf");
EntityType(this->Full, 1);
SetBodyMass(this->Full, 1);

//so that we can pass the model to the collision function.  we pass the model to cast it to coll type
SetEntityUserData(this->Full, (byte*)this);

SetEntityCallback(this->Full, (byte*)this->OnColl, ENTITYCALLBACK_COLLISION);

}
void Stone::OnColl(coll* ColObj, byte* position, byte* normal, byte* force, flt speed)
{
this->Destroy();//function call to run my collision information already set aside so that this collision testing can be easily tested
}


 

I've had this issue for the longest time, just didn't know where to turn to get help.

 

Thanks

 

~xtreampb~

bool Life()
{
 while(death=false)
 {
   if(death==true)
   return death;
 }
}

 

I have found the secret to infinite life

 

Did I help you out? Like my post!

Link to comment
Share on other sites

I see the issue. The collision callback you pass to SetEntityCallback() can't be a member function of a class. It has to be a normal C function defined a very specific way. Look at my example I posted again and you'll see the C function at the bottom of the example. The one declared with _stdcall. That's the way LE requires the callback to be defined.

 

I understand your question now about why polymorphism is required. Now that you know the callback MUST be a normal C function, once you are inside that function you have to ask yourself how can I get the classes that these Entity parameters belong to? That's why we do the SetEntityUserData() in our classes. It's a way for us to associate our object with the LE Entity. So once we are inside the C collision callback, we can get our object with GetEntityUserData() on the entity parameters that were passed to it by LE, cast back to our base class, and call the virtual collision function (which will in turn call the correct objects virtual collision function because of polymorphism).

Link to comment
Share on other sites

ok so now i can compile, but when a collision occurs i get a read/write errors. I moved the collision into my main C++ file and when i create the objects in my game, i 'link' it to the collision function at the top of the code



void _stdcall Coll(TEntity *entity0, TEntity *entity1, byte *position, byte *normal, byte *force, flt speed)
{
coll *Obj1 = (coll*)GetEntityUserData(*entity0);
//coll *Obj2 = (coll*)GetEntityUserData(*entity1);

Obj1->OnColl(/*Obj1,*/ position, normal, force, speed);

//return 0;
}

//inside my game function

Stone mStone;//derived from coll as you saw earlier.\
SetEntityCallback(mStone.GetFullModel(), (byte*)Coll, ENTITYCALLBACK_COLLISION);

 

thank you all for your help

 

~Xtreampb~

bool Life()
{
 while(death=false)
 {
   if(death==true)
   return death;
 }
}

 

I have found the secret to infinite life

 

Did I help you out? Like my post!

Link to comment
Share on other sites

entity0 and entity1 should not be pointers. They are just normal values. TEntity isn't really even a C++ class. It's just a typedef that really is just char* type. I also assume 'coll' is your class name? I ask because normally class names start with caps and have more meaningful names. Not sure what coll means. Seems like it would mean collision, but not sure why you would have a class called that. Unless you are more making an interface for collisions.

 

 

void _stdcall Coll(TEntity entity0, TEntity entity1, byte *position, byte *normal, byte *force, flt speed)

{

coll *Obj1 = (coll*)GetEntityUserData(entity0);

}

Link to comment
Share on other sites

Yep, it's throwing the error because Obj1 isn't pointing to anything because you are using pointers as the entities when you assign Obj1. So the line where you GetEntityUserData isn't returning a valid object because it's not expecting a pointer like that, because the function definition isn't expect a pointer to those entities. Change it to what I have above and it should work.

 

What IDE are you using? If Visual Studio put a break point in the C callback function and after you call the GetEntityUserData() statement mouse over the Obj1 variable and it will probably be bogus.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...