-
Posts
7,936 -
Joined
-
Last visited
Content Type
Blogs
Forums
Store
Gallery
Videos
Downloads
Everything posted by Rick
-
How did it look before?
-
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.
-
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); }
-
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).
-
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.
-
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); }
-
You can do a decent HUD with 2D drawing commands. DrawImage() would be your friend there. You could do a 3D plane if you wanted to but it's a little more involved than just using DrawImage().
-
Have you thought about how this would interact with source control tools? Will there be no issues with the project setup you have?
-
Raknet allows you to send data/receive data over a network. Data is abstract. It's whatever you want to send. If you want to send a position of an LE model then you would get the LE model position and send it's values over the network via Raknet. You aren't sending anything LE specific, just numbers and strings. You have to make the connection between these numbers and strings and how it translates to/from LE.
-
I like it. Very cool idea!
-
I try to say this with respect but if you are one of the developers and you're asking some of the questions you are asking, then you guys are not ready to make an MMO. Start with a smaller network game first to get your feet wet.
-
No clue, but in LE Lua a model with a script attached to it that is structured in the template that is in the Script folder has a physics update function callback in it. If you just have the 1 model then attached this script to it, put the script in the same dir and name it the same as the model, and then those functions will be automatically called.
-
Looks very quality. Love the visuals. Feels like to many interruptions at the start to teach the player how to play. I always hate those. I just want to play the game ya know. Maybe a more in game creative way to teach them? I don't mind getting hints but not game blocking hints. Maybe just popups on the lower part of the screen or something. To much lowering bridges. More creativity with the lower spell would be nice. The level ending part where it tallies things up seemed sort of slow and not exciting. Most of the time games that have these put lots of flash into these and lots of spinning numbers with crazy win sounds. They make it like we just hit the jackpot on a slot machine
-
TurnEntity ( global,local ) ? and Invisible Entity ?
Rick replied to joachim's topic in General Discussion
You could try applying the invisible material to it. I think there in an invisible.mat file in one of the LE directories that you can look at. That might work. -
I think this should help us developers appreciate what we had for so long in the Windows desktop world. Much can be said about having a platform that is able to get vendors to make good drivers for it to make things easy on us. All that is changing with mobile and it's all over the place. I'm starting to get more into javascript and HTML 5 for work and it has it's browser issues as well, but I'm really thinking it's the future of all apps. They are working on giving more and more access to these languages and it's a common language that runs on any device that has a browser.
-
To build off of what Flexman has shown, you could fake 1 update call by having an EntityManager class that stores an entity and a list of entities. Whenever you make an entity you add it to this list. Then you only register the callback to the 1 entity in this class. When that entity update gets called, you cycle through the list of all other entities. class EntityManager { private: TEntity _entity; list<TEntity> _entities; public: EntityManager() { SetEntityCallback(_entity, UpdatePhysicsCallback, ENTITYCALLBACK_UPDATEPHYSICS); SetEntityUserData(_entity, (byte*)this); } list<TEntity>& GetEntities() { return _entities; } void AddEntity(TEntity e) { _entities.push_back(e); } }; void _stdcall UpdatePhysicsCallback(TEntity ent) { EntityManager* mgr = (EntityManager*)GetEntityUserData(ent); // now you can call mgr->GetEntities() and loop over them all to process or maybe just call a function in the EntityManager class that will loop so that yo don't have to expose the list of entities to anything else. }
-
Looks at Source's. You make Source objects from Sound objects but Source gives you more control over vol/pitch/etc.
-
Rewerking the Asset Class -or- Back that Asset Up
Rick commented on Josh's blog entry in Ultra Software Company Blog
About that article: 1) That guy sounds like a real gem. 2) That article basically shows and explains why you shouldn't be trying to do what you are Josh. His watcher approach is something I've tried also. In my example, right before an enemy pointer was deleted (delete enemy), it would fire an event that the enemy had. When I originally assigned this enemy as a target to a wizard, that wizard would register to this delete event. So now every object that needed to know when this enemy was killed and got deleted would be notified and can then do what it needed to do with it's pointer to the object it had. In the end I didn't go this route though. I decided to make a singleton class that held all the enemies and each enemy had an int ID > 0. When I assigned the target I just passed the int ID to the wizard. Every time in the wizard class I wanted to use the target I would pass the in ID to the singleton GetObject(id) function to get the pointer to the object IF it wasn't deleted. If it was I just got back NULL and then knew that my target was deleted. I actually want to explore the event method more because I think it's more elegant and doesn't require sort of global objects in the game like a singleton is basically doing. The even way however won't work in your situation because that's more setup to tell the object that shares the pointer that the pointer is no longer valid, meaning it has already been deleted and there isn't anything it can do about it. In your case you don't want to actually delete the pointer until everything is finished referencing it. -
Rewerking the Asset Class -or- Back that Asset Up
Rick commented on Josh's blog entry in Ultra Software Company Blog
I guess my point, and the point of why some people hate COM, is that your first example is a mess. It's a nightmare of following/matching AddRef() and Release() function calls, and that's all in one place. As soon as you spread that out into many different classes and functions it gets even harder. The better option for me was to not pass and store the Texture/Wizard pointer in another class at all, but to use an asset management class and store int ID's instead to avoid pointer nightmare. I would just say adding this design will touch every piece of the way to program in C++ (what about Lua and other languages?) and is a pretty big decision. A good number of people dislike having to manually maintain the ref count. From COM wiki: "To facilitate and promote COM development, Microsoft introduced ATL (Active Template Library) for C++ developers. ATL provides for a higher-level COM development paradigm. It also shields COM client application developers from the need to directly maintain reference counting, by providing smart pointer objects." "Certain languages (e.g. Visual Basic) provide automatic reference counting so that COM object developers need not explicitly maintain any internal reference counter in their source codes." There are reasons why MS created the above 2 things. I think they realized that people hated working with aspects of COM, the ref count being one of them. Having to maintain and match up AddRef() with Release() can be a pain. -
Lua for LE won't be much different. I plan on using sqlite to do this, and I think you can also use that for Unity, but it isn't their out of box solution, but better than their persistent storage. You might want to use C++ in LE3 then because it's Lua implementation is more component like, but still offers the same great entity programming functions and style we are used to. I think it'll be a nice middle ground between the 2 styles. "You started it" "We did not" "Yes you did, you invaded Poland" lol classic
-
Alberto what specifically do you find more user friendly. I've gone back and forth a number of times. Unity has a nice structure to follow but some things like layers and such I'm not a fan of. Unity has way more 3rd party tools that really help out. I'm not all that big on math so I didn't like Unity in that respect. For me the biggest thing LE has is the entity programming. I think it's just so much easier to work with. The reason I generally go away from LE is because it has no game structure and I often found myself obsessing over that instead of the game itself. Trying to find just the right way to setup the game. In Unity it was nice because you didn't have to worry about the structure because it was provided for you. Ultimately I always come back to LE and just try to supress my obsessive compulsion with looking for a great structure for a game framework.
-
Rewerking the Asset Class -or- Back that Asset Up
Rick commented on Josh's blog entry in Ultra Software Company Blog
When we pass things to LE methods, LE needs to handle it, like it seems it was before you wanted to make the change. If we pass these to our own methods then it's on us. It's then no different than any other pointer to a class we make and pass it around for actual storage inside other classes. How often do we really pass LE objects to be stored in classes vs having the LE objects directly part of a class or just used directly without storage in a method. The one scenario I ran into was passing an Enemy pointer to my Wizard class and storing it there as it's active target. This proved to be very bad since that Enemy could die, and be deleted, outside of that Wizard class (another wizard might have had the same target and killed it). My design was poor. Passing these pointers to be stored in multiple places seems like a poor design on the developers side if they choose to do that. I think this is just inherant to working with C++ and pointers. A similar issue can happen in LE2 can it not? I can create a TTexture, pass it along to 3 different classes to be stored, then delete it in 1 class, and screw the other 2 classes that were using it. -
Rewerking the Asset Class -or- Back that Asset Up
Rick commented on Josh's blog entry in Ultra Software Company Blog
You do not need to call IncRefCount(). It's just used internally. In you example where you say pass the texture to some other user object, then that makes another reference and we would have to call InRefCount() to be correct wouldn't we? I looked up COM and it's identical to what I am doing. Maybe I will even make the syntax match it exactly. People like the idea of what COM gives them, but I think in general people hate programming in it. Which is why they've created wrappers to it for these other languages like VBA, etc. If I load a model, for example, and then delete the model, it's reasonable to expect the model's materials, textures, and shaders to also get deleted if they are no longer in use. I agree with the above for sure, but it sounded like you had that setup already, but it added some classes to your source and you were trying to keep it "clean" for potential buyers of the source. I would assume you'll have more users than buyers so if it was easier for the user before but added classes to the source, then I would think that's the way you want to go. I don't know how the internals of LE3 are setup, but some sort of singleton AssetManagement class that manages the instancing of LE objects seems like a good idea at first glance. A map where the key is the filename+path and multiple calls to Load() or new would check if the object is already loaded and handle the instance count for us. If we pass these around to other objects then it's on us to manage it correctly. Just tossing around ideas. It's tough without seeing the entire picture. On a side note, I think Orge has a good model for C++ engine. I don't like the engine itself but it's not because of how it's coded in C++. Something like the below would make more "sense" from a programmer perspective I think: Model* model = gfxDevice->LoadModel(""); Ogre uses the scene object to load/create objects but from what I remember the GFX device is really what needs to control this for LE. Either this way or you could pass in the gfxDevice to the ctors to get the functionality. The nice thing about this is that the management of the objects can be handled in the gfxDevice object and they wouldn't be stored globally. Again, not knowing how LE3 is currently setup, I'm guessing the engine objects are stored globally internally which I don't think they need to be. One might think the downside to this is that you have to keep and pass around this gfxDevice object, but I don't see that as a bad thing. Someone can make it global if they like, or pass it around to their game states. -
Rewerking the Asset Class -or- Back that Asset Up
Rick commented on Josh's blog entry in Ultra Software Company Blog
material->SetTexture(texture);// This will create a new instance of the texture Why would the above line create a new instance? Wouldn't it be Texture* texture = Texture::Load("myimage.tex"); that actually creates the instance? I can see the SetTexture() line adding to the ref count but not creating a new instance. Also, why do we have the static Load() and Create() methods again vs using new? I know there was a reason but can't seem to remember. -
Rewerking the Asset Class -or- Back that Asset Up
Rick commented on Josh's blog entry in Ultra Software Company Blog
I think Ken mentioned in the other post that doing (*var)++ should work. Haven't tried it but seems like it would. Even if that does work I personally think that's messy. If I had the choice I'd rather call the function so it's clear and obvious what's happening.