Jump to content

Mumbles

Members
  • Posts

    691
  • Joined

  • Last visited

Everything posted by Mumbles

  1. I thought that wasn't available in BlitzMaz. Reason being I didn't think callbacks were supported in BMax, and that command is by the way it works, a callback.
  2. Or recording a youtube video with a potato...
  3. In the case of the xbox (360?) Windows won't take "advantage" (if you can call it that) of the two rumble motors (it will control them both as if they were one), and it will have that irritating "LT and RT are the same axis" thing... Unless you use xinput that is - in which case, both of those are possible...
  4. I've never had explorer just crash randomly. I have had times where I've had to kill it in taskmgr, but I've found in such instances, wait 3 seconds and it seems to respawn itself. Since that involves less effort, I'll normally just wait for system to bring explorer back for me... (at least, I presume it's system doing it) I think you might need a better antivirus - one where the on access scanning is less intrusive by the sounds of it.
  5. I think "child friendly" might be a better description than just "child". Even when I was at school at maybe 11 years old, playing games like GTA wasn't exactly unheard of, loads of people in my year had it (admittedly, they were all boys, but still).
  6. Mumbles

    gui

    There was a gui library made by a member of our forum here... http://www.leadwerks.com/werkspace/topic/3473-pfwgui-source-code-now-available/ I've got my project all set up to compile with it, I've just not used it yet, but it looks nice from what I've seen of it...
  7. Well uh, that picture wasn't taken in Brighton I can tell you that much...
  8. In the DrawImage command, change 600 to -600 maybe. I remember you had to do that for nvidia cards, but no idea if that's still the case...
  9. And what if the thread author doesn't have a picture?
  10. I've never used lua, but I don't think you can do that...
  11. One of your largest problems was, LE2 was future proofed .... for programmers. I mean real programmers - the ones who understand the mainstream languages. C, C++, Java, .NET and so on, hell, even COBOL (although I've not seen LE2 running on that). However we've also got a lot of people in the world who consider themselves programmer's because they've written a macro in MS Office. Others have built webpages in HTML (with a rare few in PHP), thus we have yet more programmers who have panic attacks at the sight of int i = 0; But one of the worst things I think is happening is actually things like FPS Creator. It's great when you've never made a game before but within weeks you'll probably have made just about every type of game it can make. I use that one as an example because in 2001 I started with Dark Basic, so it came from the range of products I was familiar with (It was my 15th birthday present). I used that for about 5 years before upgrading to DB Pro (and then 2 years before moving here). In that time I remember looking at FPSC, thinking "But this is just 3D Game maker all over again". To my shame I've got a copy of that somewhere too, I used it for all of about 20 minutes. Sad truth is, there just isn't yet any decent package that really lets you create a customisable game without any programming knowledge. We're getting closer but those that are, are still heavily reliant on proprietary scripting languages. As a programmer, there won't be a single engine available any time soon, that I can use on my own (without any artists/modellers) and produce excellent results, neither will the be an engine that artists or modellers can use without any programmers. Would you want to join a team when you don't even know your own capabilities, and what you can bring to the project? Would you want someone else to join you when you're unsure of their capabilities? And if several people did join together, whose project would be developed first? Would they hang around once theirs had finished? Game dev, whilst we'd all love to develop our own games, is something that requires co-operation. So long as LE3 doesn't go the route of single user "Klik & Play" type, should be interesting to see. The largest problem is, that's what the market demands. It comes down to whether Josh wants to be immortalised in history for building the most powerful indie-friendly engine ever, or whether he wants to keep a roof over his head... However I'm under no illusions, that anyone has really maxed out LE2's capabilities. It was just a little too good for its time, sort of like id Tech 3 - no way would I ever have imagined when I first saw Call of Duty 4, that the underlying graphical rendering technology was an updated version of something that was 8 years old...
  12. Where speed isn't so crucial (RTS games for example) it does everything that reliable, ordered UDP does, but it's not handled by your application, it's basically handled by the OS. It's almost sure to be better optimised if it's part of the OS rather than having to do bits yourself. Currently I'm looking towards using both UDP and TCP side by side, both performing different tasks, but working in harmony: UDP for the vast majority of the data: Clients telling the server how they wish to move, or interact with the world The server keeping all clients informed about updates to the world, and other players. (These are only delta updates, up to 40 per second default) TCP for everything else: The server sending definite (timestamped) states of the world for clients to base their received updates from (no faster twice a second) Players chatting Downloading extra packages from the server (File transfer is one case where there is no point reinventing the TCP wheel on UDP). Latency measuring (Yes, it could go on the UDP socket, but I'm keeping that socket as clear as I can, it already has a very high frequency of packets on it) As for when would anything be ready to show? Months... at least. I've since found out that actually, winsock's not as bad as I first thought. You don't have to wait for the peer to shutdown their socket before you can delete it. Using setsockopt you can change a value called SO_DONTLINGER to true (or 1 because it's C not C++). Now you can shutdown your socket as normal, but if you then proceed to call closesocket() before the remote peer shuts down, rather than crashing your application, it gives the remote peer error 10054 "connection reset by peer" on their next call to send() recv() or shutdown() It's just that SO_DONTLINGER is set to false (0) by default, but once it's changed you can pull the plug on any player you don't like without any warning, and without having to wait for them to shutdown too. There's also probably less to go wrong with this method than conditional accept, so I may end up not using that. Yes, it confirms to a different sort of trouble maker that your application is actually running, but I think that's probably too insignificant to spend too much time worrying about.
  13. Online games... The unsavoury types are always drawn to them. As a game admin, you simply kick them out of your game, or ban them if you're in such a mood and that's the end of it. But now looking at it from the developer side of things, I can't help thinking a bit more about it. It's one of UDP's advantages over TCP. When you get a known trouble maker trying to join your game, your server can either tell them to sod off (in the nicest way possible), or it could pretend that it never even got the request. Easy. What about when you have TCP, or both? TCP can't just "ignore" a connection request* - it's just not allowed. All you can do is either call accept, which will (normally) block until a connection request actually arrives -- or not, in which case, no one can connect, not even the good guys... It's something most people here won't need to think too hard about, but perhaps a bit if input for those who it will affect (namely: me) Most games using a TCP connection system, will continually call the accept function, since it wouldn't be much of a multiplayer game if only the host could play. When a known trouble maker comes along, the server simply closes its side of the connection and refuses to send anymore data. The trouble I learned recently is that you can't actually force the remote peer to close their end of the connection - and you can't delete your socket handle until they do shut the connection, in case they are still sending data to you... Now I know that winsock isn't really built for games. For starter's Nagle's Algorithm starts enabled, and it's highly recommended in the MSDN that you leave it enabled. Yes, it's easy to disable (setsockopt with TCP_NODELAY set to false), but the fact you have to manually disable it at all, says it's not what winsock is really targetted towards. So, already I've realised that once an undesirable gets in, I could in theory have them wasting a valid socket resource (if they're not using an official client), by refusing to shut the connection and waiting for the timeout to occur. By recently I found a way round this... Winsock allows something called "conditional accept" which is a callback called by the accept function, where you can look at the sender's IP and port (although the port isn't really helpful at this stage) and decide whether to open the connection, or to refuse it - Thereby causing the socket to get error 10061, as if the server application wasn't running. A little more reading though, and I discover that this conditional accept thing is on the list of "Lamest things supported on win32". Without conditional accept, as soon as a connection request arrives, it's accepted automatically in kernel space, so it's about as fast as it can be, whereas the conditional accept callback is done in user space, and switching between them many times a second takes time. The argument is, that it would be so slow that genuine users can be getting 10060 timed out errors due to the accept not being handled fast enough. Anyone here know anything about kernel stuff? I know nothing about it, but when I asked my brother, his response was "anyone who thinks that switching between kernel space and user space is slow, probably thinks a kernel has something to do with fried chicken". He also suggested that even with several hundred users, a well optimised system would see no slow down in accepting new users via this "slow" callback, and that it would probably only fall over during a SYN-flood (but not using the callback is just as vulnerable). Personally, I can't see why other TCP games haven't used conditional accepting. It's been around for years, so professional companies must think there's something wrong with it. But what? It seems like such an obvious solution. Why do we need to say: "no, you're not allowed in... Go away please" when we could simply pretend there's no one home (Same as they can do on UDP)? *Actually TCP can ignore connection requests. During the conditional accept callback simply don't return a known value (CF_ACCEPT or CF_REJECT) and your socket will get error 10061 (rebind the socket and then you can carry on using it) but their socket will get 10060, as if the server wasn't even switched on... However, it's a cheap hack and probably not a good idea to use it, since this is undocumented behaviour.
  14. You can protect yourself from the 12/21/12 thing by not using the US short hand date format 21/12/12 ... Nope, that doesn't work 12/12/21 ... Doesn't work either Crisis averted...
  15. I'm not shy about the fact that I always thought 60 was completely ridiculous number. Building something that I hope to actually be finished one day, WITH multiplayer capabilities, I thought 50 would be a better number, as it's much more scalable (every 20 milliseconds as opposed to every 16.666667, Or 17, 17, 16, 17, 17, 16 etc depending if you use floats or ints). But looking at commercial game developers, they think even that is too high. I've sort of got raycast projectiles working (I say sort of, because I can't see them breaking at present), and of course for "moving projectiles", the length of the ray (which translates to how fast it travels) is affected by the number of physics steps per second: 100 steps per second, ray is one unit long 50 steps per second, ray is two units long 30 steps per second, ray is 3.333333 units long etc But what about player movement? There's no raycasts involved here. Players are represented using ellipsoids that are stretched tall. They are actual physics bodies that are kept upright using Newton's custom 6DOF joint (which there is no access to in LE2, but I've made them more or less the same as upvectors, which are very similar to LE2's raycast player controllers). All rotation is restricted by the joint, which prevents them from toppling over but they also can't "look to the side" for example (your camera can as its rotation is not physics based at all, it just has its position locked to your player body). I set about updating the physics update section of the main loop, which is rather small. Once every (1000 / tick rate) milliseconds (based upon the time of the first ever tick, not the time of the previous tick), the world is updates by (1 / tick rate) seconds. So if tick rate is 50, then once every 20 milliseconds, the world updates by 0.02 seconds. I swapped the tick rate number out for 40 instead of 50. It's still divisible by 1000, but now the world will advance by 0.025 seconds once every 25 milliseconds rather than the old 0.02 seconds every 20 milliseconds. That sounds right doesn't it? The first problem to arise from this, I'd already expected. I hadn't updated the amount of force that each player moved by. This was deliberate. If the force value was a number per second, than the code wouldn't need updating. But it wasn't - it's just an arbitrary number. So the next step would be to calculate the force required, such that were it was 1 before, it would now be 1.2. I preferred a calculation over the fixed number version because once I had got the same distance per second working at 40 ticks per second, I would then change it again to see if it still worked. There is however, a complication. A force of 1.2, 40 times per second, does not give the same results as 1.0, 50 times per second. The most logical reason I can think of because once the next physics step arrives, I add a new 1.2 to whatever force was still acting on the body from the previous update. This in theory should be fine if there was no cap on the maximum speed a player is allowed to travel at. If you add forces on the bodies for however many ticks 2 exact seconds works out as, and then let them slow down naturally, they should stop at the same position. But I've imposed a maximum running speed cap to prevent players from speeding up like motorbikes and reaching silly speeds. I played around for a few hours trying to come up with some exponential formula to allow deceleration to occur at the same rate, regardless of the update rate, but without removing the speed cap, it just wasn't going to work (and with the speed cap removed, the complex formula shouldn't be necessary at all). Eventually I tried a different idea. Instead of trying to change how many times per second the physics are updated, why didn't I keep that constant along with the forces, and instead only change how often the forces were calculated? To make this idea work I'd have to remove the current system and replace it with a new one - one that ticked much, much faster - insane speeds by comparison. Well that's exactly what I did. There were 1000 physics updates performed per second. (you read that right, but I'll write it out in words just to make it absolutely clear, one thousand times per second). I know what you're thinking: "you think you can make a game playable over the Internet when it's updating 1000 times per second... You're mad!" I believe Einstein went mad too, but this isn't mad. I'm thinking along the idea of 1000 little steps per second. For a case of 50 ticks per second, we calculate "movement" forces, and then for the next 20 ticks we keep giving the objects the same movement forces. If our force is equivalent to 0.1 units per physics mini-step, then after 1 second we will have moved 100 units if we kept the movement key held down for the entire time. The same is visible if we calculate the forces 40 times per second instead - there's still 1000 mini-steps occurring each second, it's just that the forces are being calculated less regularly, but if you don't alter your movement vector, the result is identical. It means that now, the I can set the world's apparent tick rate based on how the game is being played. Over the Internet, 25 seems like a good number here, and for local network play, 40 is probably plenty fast enough (For reference, Unreal Tournament uses 20 and 35 respectively, but I thought 35 wasn't very scalable). For single player games, it depends on the style of game here, but for a FPS, I don't 50 (or even 100) would be excessive, however I feel multiplayer co-op story based games should probably stick 40. There's a lot of information to keep synchronised there, and it gets worse when there are more than 2 players involved. However, things to consider if using the mini-steps idea. "external" forces, such as being sent flying by a rocket explosion should only be applied for 1 tick. The explosion only happens once - it's not a persistent force like walking would be. Keep your internal and external forces separate - add them both during a mini-step, but afterwards, clear the external forces, but keep applying the same (not recalculated) internal forces until your next "major" step. It makes no difference if (for example at 40 ticks per second), you really do update the world once per millisecond, or if you simply call the update function 25 times in a loop, and then not perform any more mini-steps for another 25 milliseconds. Using the latter method may help to remove the temptation to recalculate forces before the next major step, and closer resembles the layout of our 40 ticks per second behaviour. It also means your main game thread can sleep, thus leaving your client threads with more CPU time to accept your clients' (or AI bots) inputs. In a very large world with many bodies, 100 mini-steps per second may be more preferable than 1000, but there will be some loss of accuracy here, or you'll need a system to know when to add an extra step. After all, now each major step will occur after 2.5 mini-steps (in case of 40 per second), so you'll need to keep track of when to add one extra. It's easy for 40, all major steps have two mini steps, but every second major step will need one extra - but if you want 17 (just as a deliberately awkward example) major steps per second, that will be a little trickier, although not impossible.
  16. Not the parent class destructor? This is confusing... I thought virtual had no effect on classes that had no children... Mind you, not as confusing as this: I went to click the reply button, but instead clicked report (I don't think anything happened, but sorry anyway) and I got this message: [#10136] You do not have permission to report the item you are attempting to report. So if there actually was something nasty, who DOES have permission to report it?
  17. I tried but the link pointed to my blog entry... A bit confusing since I've never heard of them before, but I think I'm starting to get the idea of what they are... I put "c++ virtual destructor" in Google and found this: http://www.programmerinterview.com/index.php/c-cplusplus/virtual-destructors/ And I think Roland's answer says the same thing. So I can see why I might need it. Until I read that, I always thought that both constructors AND destructors were NEVER inherited from the base class because they had no name, but always had to be available if you wanted to use it. I think I've got a few classes to upgrade... Regarding your improvement to the struct... I've never seen that before... That's effectively a constructor? Might make things a bit neater, but normally if I return a pointer from an function (or member function), it will be allocated and initialised as well, (unless I return 0 back, in which case no memory was allocated)... But actually, I've noticed that the classes I put up earlier are in the middle of being changed. Notice the RayObject::CollisionHitResponse * CollisionHitData that has protected access? Notice how it has no accessor? "whoops" I was in the middle of re-designing it. Until then TestForCollision used to return a pointer (you had to declare it first, and then free it when you had finished), but I changed it to the pointer being a member instead. The new idea is that it will automatically free during ~RayObject() which hasn't been written yet. The pointer is nulled during the Initialise method and as long as it stays null, it hasn't hit anything. If it hits something it allocates and initialises the struct - normally that would end the projectile's life (at the start of the next physics tick), but if not it can go back to a null pointer (after freeing it) once it's no longer colliding. In fact, breaking news (international alert) I'm not even sure it needs to be a pointer... the NumEntitie**** field can act as the flag (zero = miss, non-zero = hit) - and if it's non-zero call realloc on the two pointers to make them large enough and then fills the data. So one less allocation when creating a RayObject (ie one less bit of cleaning up when finished )... Sounds like I've got quite a bit of festive coding to do...
  18. That's probably my java upbringing - we were never taught that at uni, so I just translated it directly. But now you mention it, it's so obvious... (If you're supposed to do that in Java too then I blame my uni lecturers) That's what the Initialise method does - all constructors call that. If there are derived classes, then L1, is for the base class's variables, L2 is for all which directly inherit from. L3 for all which inherit from that class etc (could cause confusion with multiple inheritance but right now, there is only single inheritance), since they can't have the same name without making it virtual and redefining. You've linked to this post there and I've never heard of virtual destructors before. Is this because I'm not modifying the string (which I'm not, it's read only) or is it something else? I know const means (I think) 5 different things but the only one I ever remember is immutable variable... If I took the time to design it carefully I could probably make it so that they were only in scope when not on the front menu. However, I'm lazy and find it easier (in this case) to give them global lifetime, and simply reset the list when starting to load (and unload) a level. The list shouldn't get that big from a memory point of view, but will have entries rapidly appearing and deleting many times a second. In a way I suppose I'm building it a bit like UT 99... You can be in the middle of a level, press escape and up comes your front menu (but the level is still there in the background - and in a multiplayer environment, very much alive and ticking). I'm basically building the same way, just starting with an empty level, rather than no level. This is whay I always say, I might be able to write a program in C++, but no way am I a pro... I've still got a lot to learn
  19. Those pointers have no owner. They are akin to bookmarks, with the struct akin to the book's pages - the book doesn't read itself (in my mind, that sort of behaviour denotes a class), instead they book is read by someone. I could set up an object to read the book, but since I'll only have one book reader object I feel it's overkill to set up a class, when a procedural interface will perform almost identically. Instead I use the visible functions to alter the list and traverse through it. They have global lifetime, but with limited scope so I'm not tempted to try and write the pointer values whenever I feel like it - as I'm sure you'd agree, doing that would be loony... However please don't think I'm "anti classes". For completeness, the projectile list points to a RayObject. But the RayObject's test for collision is very different based on whether it is a "hitscan" or moving "projectile". The list relies on polymorphism and virtual members to iterate through the list once rather than two lists, one for hitscans and one for projectiles. This can't be done without classes. Plus due to the sheer rate that these objects will be created and destroyed - and due to their "active" nature ("the book reading itself", or "the projectile moving itself") they're definitely suited to classes, and I wouldn't dream of trying to implement these in a procedural way. //Callback prototypes used by this class //The prototype for the collision test "prefilter" callback. Prefilter means a ray entered a body's AABB, should this body be accurately checked (slow) for collision, or disregarded now? //This can be used (for example) for a lightning beam, which starts at the firing player's origin, to stop it hitting the player that fired it. Since it would be overkill to try and ungroup that player from the players group just for the duration of the lightning beam collision check typedef unsigned int (* RayObjectCollisionPrefilterCallback)(const NewtonBody * body, const NewtonCollision * collision, void * userData); //The prototype for the collision test "process hit" callback. Process hit is called for a body which was not discarded from the prefilter, and where the ray hit actual geometry (as opposed to entering the AABB, but then completely missing all faces). This allows us to figure out where the collision was in relation to the body's origin, so we can add the "throwing" force in case of explosions typedef dFloat (* RayObjectCollisionProces****Callback)(const NewtonBody * body, const dFloat * normal, int collisionID, void * userData, dFloat intersetParam); //A flexible and adaptable struct for when TestForCollision returns. Your own filter callback will decide if the raycast is an "all body" "closest hit" or "first hit" type. However first hit is not recommended for projectiles since there may be others closer, but with a smaller AABB struct CollisionHitResponse { unsigned int NumEntitie****; Entity * EntityHit; //unsigned int NumCollisionPoints; //A collision point is only saved if the entity the ray collided with is also saved, so these "count" variables will be the same, making one redundant - in this case, I chose this one. TVec3 * CollisionPoint; }; //The projectile class is a ray, rather than a body. It has no NewtonBody, but may have an associated TEntity //The ray might be "hitscan" (living for only 1 physics tick, for example a laser beam), or it might gradually move forward (like a rocket) //Was renamed to RayObject almost instantly //class RayObject : public Entity class RayObject { public: RayObject(std::string EntityConfigFileName, std::string SceneName = ""); virtual void TestForCollision(void) = 0;//Returns a pointer to a malloc'd CollisionHitResponse struct if a valid collision occured (remember to free it when finished), otherwise returns 0 (and nothing was malloc'd, so nothing needs freeing) bool RemoveThisRay(void);//After all collisions have taken place, ray objects that have collided are deleted. bool IsExplosive(void); void SetDeleteTime(void);//Adds the zombie time to the current time. Once the current time is higher than the delete time, a projectile will be deleted unsigned int GetDeleteTime(void); protected: bool ExplodesOnContact; bool LifeExpired; TVec3 Position; TVec3 Rotation; virtual void Move(void){return;} //Hitscans can't move so they inherit this definition and just return instantly, projectiles will redefine this member unsigned int ZombieTime; //How long does this projectile remain drawn once it's expired? Used to prevent the laser and lightning beams from "flickering". All projectiles and most other hitscans will set this to 0. It can only be set during a constructor, and a projectile constructor will forcibly set it to 0 without even attempting to read it from the config file unsigned int DeleteTime; //Once a ray has it's delete flag marked, this time will be set, once this time is reached it's deleted. CollisionHitResponse * CollisionHitData; bool HitDuringUpdate; void MarkForDeletion(void); unsigned long long ECB; //Previously these two were passed as parameters to the TestForCollision function. //Now they are instead set during the constructor, read from the entity config file. However, it's not possible to users to write their own callback functions. Certain strings will "map" to different callbacks - only the predefined callbacks are valid. //However it would allow people to create an instant hit shotgun, by using the machinegun callbacks for their shotgun - so that kind of mod-ability will still be present RayObjectCollisionPrefilterCallback PrefilterCallback; RayObjectCollisionProces****Callback Proces****Callback; private: void InitialiseL1();//Only the correct constructor can call this. Constructors for derived classes can't }; class HitscanRay : public RayObject { public: HitscanRay(std::string EntityConfigFileName, std::string SceneName = "") : RayObject(EntityConfigFileName,SceneName); void TestForCollision(void);//Test for collision takes the possibility of limited range into account. Once finished, ray will be marked as ready for deletion. At which point once its zombie time is expired, it will be deleted protected: unsigned int Range; //Tenths of a unit, so 10 = 1 unit. private: void InitialiseL2(); }; class ProjectileRay : public RayObject { public: ProjectileRay(std::string EntityConfigFileName, std::string SceneName = "") : RayObject(EntityConfigFileName,SceneName); void TestForCollision(void);//Once finished, checks the expire team, and if expired, deletes the ray -instantly- protected: unsigned int ExpireTime; //Time after which the ray expires. The weapon which fired the ray will store the -length of time- the ray is valid for (in milliseconds), when creating the ray, it looks this up, and adds it on to the current game time. After a check for collision, the time is checked: if the life has expired then the ray will be marked as such, and deleted at the end of the physics tick unsigned int Speed; //Tenths of a unit per second, so 10 = 1 unit per second. Not affected physics update rate private: void InitialiseL2(); }; The pointer to base class makes it a breeze. Just tell each RayObject to TestForCollision and they will run their correctly defined version classes form the majority of my project. I think more than structs and namespaces combined... It's just static classes that have always irked me. For me now, static classes as a means of access protection is no longer necessary. I'm starting to get the idea that this secret namespace idea was much more common before Java came about than it is now. Sadly, it didn't translate directly into Java, whereas Java's workaround for it did translate directly back into C++, therefore you get more uniform code between the languages if you use the static class method. Of course I'm too young to know if that's actually true or not, but it sounds like it might hold water.
  20. The pimpl idiom looks almost identical - the only difference being the obvious: classes instead of namespaces. Thus just boiling down to each individuals preference in semantics. Certainly to me, this doesn't look any less confusing. That said, your example hasn't quite made the class MyObjectPimpl totally secret. If this was defined in a header file, then if I included the header file in an external source file, I could write: YourCompany::YourProject::YourLogicalNamespaces::Details::MyObjectPimpl * APimplObject = new YourCompany::YourProject::YourLogicalNamespaces::Details::MyObjectPimpl(); ...and I've just got myself an instance of a class that I'm not supposed to be accessing... But I also wouldn't type this by accident and it's accidental mistakes I'm trying to protect against. So really, I'm just nitpicking here, it's not really a valid criticism But by converting the Details namespace into an unnamed namespace, it's impossible to fully qualify it from outside YourLogicalNamespaces. Thus MyObject can create instances of the pimpl, but no one else can. I learned about unnamed namespaces here - the second answer (which builds on the third answer) http://stackoverflow.com/questions/9321/how-do-you-create-a-static-class-in-c
  21. Well, I've just learned something too, from the comments... I thought that access specifiers were invalid for structs, and that all struct members had to be public. Apparently not... The advantage is probably none in terms of performance. It's just the way I like to work... I've always thought of classes as "collections", and a collection with just one thing always seemed silly to me. It's like having a shelf reserved just for trophies, and then only putting one trophy on it. I'm fine with classes having some static members, but a class with only static members never felt like a "proper" class to me. So I guess it really is just semantics... The advantage to me is that it prevents the private members from showing up in intellisense. Yes there are icons in the autocomplete list which tell you which members are public, and which are not, but I'm guilty of choosing the name that sounds right and then just tabbing it away, without actually checking if it has the right access specifier. And I must say, I would prefer that LEO::Draw namespaces arrangement over classes, but that's probably something unique to me. Except for maybe Images. Personally I might have made Image a class, with a Draw method. But points or lines, no way. A surface might be a class, which contains DrawPoint(), DrawLine() etc public functions though. But remember, LEO isn't just built with me in mind - really it should be the people using it, who decide if they prefer the static class or the namespace approach.
  22. I prefer smaller code over faster code anyway... 3 GHz is more than enough these days, slow downs are more likely to be caused to sloppy programming leading to huge time complexities and I doubt any compiler could really fix that for you... And really, there's no need for stuffy 10 MB exe files when it comes to games, 150 KB is more than sufficient, but call it 2 MB in the case of games with a statically linked engine (which you can't with LE 2, but for those others that can/do)
  23. It's something I've always been opposed to - creating a class, knowing full well I'll only create one instance of that class, or none at all. I've always been an advocate of the idea "only create classes if you want to create several instances of the class". It's what always kept me away from the idea of using LEO, instead of using the plain C interface. How many "engines" are going to be running inside my game? Just the one, so why make an engine class? Despite saying this, for years I did have some of my own 1-instance classes - usually some sort of linked list (I know these exist in the STL libraries but I'm a bit of a nutter and like to reinvent even basic things). I'd set them up as classes because I wished to hide some data, such as the pointer to the head of the list, so that it was always valid and could never be tampered with accidentally. The private access specifier was great for this, and anything else I never wanted to call/access accidentally. Sadly, these private members always showed up in intellisense and it even offered to autocomplete them for me, only to then tell me that I was accessing a member function that was out of scope. Not hugely serious, as it was only a quick few seconds to replace the bad function call with the right one, but a little irritating nonetheless. The lergest problem I was running into was that I noticed I lacked consistency. Half of these list classes I'd designed with the intention of being "static classes" and the other half expected an instance. The was no compelling reason to have chosen either, and was probably determined each time by how much stamp glue I'd been sniffing in the previous hours. I turned to Google in an attempt to standardise my classes and find out in no uncertain terms which was the better way, and then update my classes to reflect this. I soon found though, that really, in the land of C++, neither was really the right option. Static classes were a sort of "hack" solution for Java (which is what we were taught at my uni) to achieve the same thing that C++ achieves with anonymous namespaces. Well, I call them anonymous namespaces because it sounds fancier than unnamed namespaces which is their correct name. Indeed, I'd always known that namespaces, didn't actually have to have a name (even though the word namespace kind of implies it does), but I'd never seen any advantage of them. Since discovering how to use them "properly", I sort of couldn't keep it to myself, so hopefully any other C++ users out there who thought the same as I once did, could learn something here. Here's one of my list classes (header only), as a list. It's one of the simpler lists, which much more resembles an STL linked list, than some of my others do class RaysList { public: RaysList(); ~RaysList(); void AddProjectile(RayObject * RayToAdd); void DeleteProjectile(RayObject * RayToRemove); void DeleteExpiredProjectiles(void); void DeleteAllProjectiles(void); RayObject * GetNextProjectile(void); RayObject * GetCurrentProjectile(void); bool EOL(void); void ToStart(void); bool IsEmpty(void); private: struct ProjectileListEntry { RayObject * AssociatedProjectile; ProjectileListEntry * NextListEntry; }; ProjectileListEntry * ActiveProjectileHead; ProjectileListEntry * MemToReadFrom; }; This one as it happens, has no statics (decide for yourself how much glue was involved beforehand), and it looks simple enough. There's a constructor to set the two private pointers so they're never pointing to invalid memory (0 being hardcoded to mean end of list, thus is valid) There's a destructor to free any pointers that were allocated, should the list fall out of scope The member functions are all public so they can all be called validly But it can be re-written without a class, whilst still keeping the access specifiers. "How?" You might ask, because it's true that public, protected and private (well, and friend too) are all class access specifiers - they can't be used for structs or namespaces. Well, here's how: (Header first) namespace RaysList { void AddProjectile(RayObject * RayToAdd); void DeleteProjectile(RayObject * RayToRemove); void DeleteExpiredProjectiles(void); void DeleteAllProjectiles(void); RayObject * GetNextProjectile(void); RayObject * GetCurrentProjectile(void); bool EOL(void); void ToStart(void); bool IsEmpty(void); } No pointers at all here, but somehow my functions are going to return some. The magic takes place in the definitions (the .cpp file) namespace RaysList { namespace { struct ProjectileListEntry { RayObject * AssociatedProjectile; ProjectileListEntry * NextListEntry; }; ProjectileListEntry * ActiveProjectileHead = 0; ProjectileListEntry * MemToReadFrom = 0; } void AddProjectile(RayObject * RayToAdd) { ProjectileListEntry * NewEntry = (ProjectileListEntry *) malloc (sizeof(ProjectileListEntry)); NewEntry->NextListEntry = ActiveProjectileHead; NewEntry->AssociatedProjectile = RayToAdd; ActiveProjectileHead = NewEntry; } } The rest of the functions haven't been included, but for this demonstration they don't need to be, this one function shows you how it works. At the top of the file (not shown) is a line to include the header file Again, we open a namespace with the same name, but immediately inside this namespace, we create another one, without a name Anything inside the RaysList namespace has total unrestricted access to the secret namespace So, because of this, the pointer declaration to the secret struct in the first line of AddProjectile() is valid But if were to try and declare a pointer to this struct from outside the namespace it wouldn't be valid, and it wouldn't even show up in intellisense, so you wouldn't even be tempted. Even the lack of a destructor doesn't cause a problem, since the pointer variables are declared outside of functions, thus they never fall out of scope... So you don't have any memory leaking. The only reason we might have wanted a class for this concept... Eliminated... I'll never create a "static class" ever again So there you have it. If you want the same effect as the private access modifier, but don't need to create an entire object, there's a way. Sadly, because I learned Java at uni, I always thought this should have been possible, but never knew how, because of course, in Java, everything is an object, even if you're only going to create one instance of a class.
  24. Ack! Are you serious? Microsoft really has entrenched itself deep if this is true. I switched to Code:Blocks for a few reasons, but I hope I don't run into this problem for awhile. Very much so, but it depends on which libraries you're linking to. Libraries like SDL for example come with Visual Studio libs, gcc libs (and sometimes more) and source code to build it yourself. I switched to code blocks to remove the need for VC 9.0 runtimes as well as rumours that it compiled faster. However to make the Platform SDK work with it was apparently possible, but too complicated for a simpleton like me. Therefore Microsoft rubbed their hands together, looking smug as I returned to VS 2008. I should upgrade to 2010 at some point but can never be bothered getting round to it (I only like to replace things when they really are dead). Yes the compile time was a quite a bit longer but since then I found that by reorganising my includes, the compile time for compiling 81 total source/header files, a combined size of 1150 KB (excluding LE2 engine files) is about 5 seconds where it was more like 60 (about 45 on code blocks). No precompiled header, and not using an SSD, just only including only what's needed (you know, the way you're supposed to do it), instead of before where every single file (header and source) included a file called "includes.h" which included every single header file. It's more convenient to do it that way but it's a lot slower on the compiling. I also don't ship the redist installer for the VC 9.0 runtimes, instead I include the 3 dll files needed and the manifest. I learned to do that from Mika in the opening days of werkspace as a means of making my project work on Linux using wine. It turned out to be very useful since it also allows me to test it on my brother's computer - he has Windows 2000 and does NOT have the VC 9.0 runtimes, but doesn't want to install them either (he calls it "registry spam")
  25. Make a wrapper class (or several classes) for LE 2 now, so that when the change comes about, most of your main code remains intact. Rather than later having to change every instance of (for example) adding a force to an object later, putting it into its own function call now allows a quick two line (or so) changeover of said function definition when the change comes about. It's along the same reason as why you use named constants, rather than simply typing the value each time you need it. If you need to change the value of the constant later, it's one change versus potentially many...
×
×
  • Create New...