• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
  • entries
  • comments
  • views

About this blog

A hobbyist's struggle to write a simple game engine

Entries in this blog


My motivation was to write a test framework that I could run on functions to check whether they properly handle bad input. The below code sort of assumes the tested function does not throw, because that would defeat the purpose. Instead it should return gracefully and/or output a pretty error string. If you've done a bad job at coding, you'd, of course, expect it to crash.

The tester takes a set of reference arguments, which come in the form of hopefully valid input arguments that you provide. Code that runs all tests might then look something like this:void my_test_nocrash(int32 i32, char* str, testing::eTestType argtype, void* ptr, math::vec3 vec) { lout << i32 << " " << (void*)str << " " << uint32(argtype) << " " << ptr << " " << vec << endl; }void my_test_crash(int32 i32, char* str, testing::eTestType argtype, void* ptr, math::vec3 vec) { lout << i32 << " " << str << " " << uint32(argtype) << " " << ptr << " " << vec << endl; }....void* ptr = (void*)0x12345678;testing::test_function(my_test_crash, 33, "mystr1", testing::eTestType::eRandom, ptr, math::vec3(1, 2, 3));testing::test_function(my_test_crash, 45, "mystr0", testing::eTestType::eDefault, ptr, math::vec3(3, 2, 3));testing::test_function(my_test_crash, 33, "mystr1", testing::eTestType::eDefault, ptr, math::vec3(4, 2, 3));testing::test_function(my_test_nocrash, 34, "mystr0", testing::eTestType::eDefault, ptr, math::vec3(2, 2, 3));testing::test_function(my_test_nocrash, 22, "mystr0", testing::eTestType::eDefault, ptr, math::vec3(5, 2, 3));
Essentially I call [font='courier new']testing::test_function(your_func, ...args)[/font] with of arguments that I'd normally pass directly to [font='courier new']your_func[/font]. There are no glaring restrictions on argument types, but emulating bad input from non-trivial types takes a bit more work and requires modifying the return value modifiers. As such, the tester in its current form serves best to try out bad scalar, string and pointer values.

The above code runs five different types of tests on two functions (as the two last modes would crash if the [font='courier new']char*[/font] wasn't cast to a [font='courier new']void*[/font] and passed it on to the output stream as a string). The snippet also hilights the five profiles I wrote for myself, of which two are largely useless. Here's a brief rundown of what each one does:

[font='courier new'][color=#57a64a]/// [/color][color=#57a64a]eDefault[/color][color=#57a64a]: arguments are passed to the function without change. This is as if the function[/color]
[color=#57a64a]/// was called directly with the arguments provided.[/color]
[color=#57a64a]/// [/color][color=#57a64a]eNull[/color][color=#57a64a]: each argument is converted to the equivalent of NULL, which is often a valid,[/color]
[color=#57a64a]/// if undesirable input.[/color]
[color=#57a64a]/// [/color][color=#57a64a]eRandom[/color][color=#57a64a]: input values are "randomized", but their type is not compromised. This means:[/color]
[color=#57a64a]/// * a string will still passed to the function as a string, containing the word "[/color][color=#57a64a]<>[/color][color=#57a64a]"[/color]
[color=#57a64a]/// * pointer values are reverted to NULL so as to not cause unwanted crashes[/color]
[color=#57a64a]/// [/color][color=#57a64a]eUninitialized[/color][color=#57a64a]: arguments are assigned values you'd expect the debugger to assign[/color]
[color=#57a64a]/// uninitialized variables:[/color]
[color=#57a64a]/// * strings and pointers become "FDFDFDFDFD"[/color]
[color=#57a64a]/// * scalar values are assigned the equivalent of -1[/color]
[color=#57a64a]/// * floating point values are assigned NAN[/color]
[color=#57a64a]/// [/color][color=#57a64a]eGarbage[/color][color=#57a64a]: all argument values are fully randomized. If the function call expects pointers,[/color]
[color=#57a64a]/// this is pretty much guaranteed to crash the program.[/color][/font]

In most cases, running the function with eNull is sufficient, but eRandom might be useful to test how it behaves with ill-formed input. eUnitialized and eGarbage are likely to crash the program outright and frankly I can't really think of a use for them in normal circumstances. Of course you can always modify or add different test types to match your criteria.

Here's the output for the above test run. It's not pretty, but hey - it's not coding that's about the looks - it's the final product.


[Testing] Running eDefault: 1/1

33 Everybody <3 unicornses. 4 12345678 (1.00, 2.00, 3.00)

[Testing] Running eNull: 1/1
0 00000000 2 00000000 (3.00, 2.00, 3.00)

[Testing] Running eRandom: 1/3
-721371808 <> 2 00000000 (4.00, 2.00, 3.00)
[Testing] Running eRandom: 2/3
-477658674 <> 2 00000000 (4.00, 2.00, 3.00)
[Testing] Running eRandom: 3/3
-842655645 <> 2 00000000 (4.00, 2.00, 3.00)

[Testing] Running eUnitialized: 1/1
-1 FDFDFDFD 2 FDFDFDFD (2.00, 2.00, 3.00)

[Testing] Running eGarbage: 1/1
1073937667 B73BBFE2 2 A06BD497 (5.00, 2.00, 3.00)[/quote]

With that aside, here's a quick rundown of how it all works:

  • wrap the function call up in a template and patch into the argument list using an initializer list. A [font='courier new']std::vector[/font] works nicely for this.
  • the argument list never leaves scope, so what the vector does is use a proxy container ([font='courier new']value_proxy[/font]) to store a pointer to each argument. Each argument is referenced into a [font='courier new']T*[/font] in [font='courier new']value_proxy[/font]'s templated constructor and assigned to an internal [font='courier new']const void*[/font] storage.
  • call the function, using the vector as a new proxy argument list. Since partial specialization of functions is not allowed, use a proxy class ([font='courier new']call_modified[/font]) to achieve that.
  • use the cast operator in [font='courier new']value_proxy[/font] to intercept the value when the argument is being forwarded and change the value types you want to what you want. You can write these for any type by modifying the __return_XXX macros. eDefault mode should work out of the box for most things, although I haven't tested it all that heavily.
  • the whole thing is header-only, depends only on and optionally whatever fancypants logging aggregate you might be using. I modified the attached code to use [font='courier new']cout [/font]so it should compile and run out of the box. For the provided code you'll also have to drop in your own rng functions, which are currently dummies.
  • the number of arguments a function can have is limited by how many overloads of [font='courier new']call_modified[/font] are implemented. I've implemented templates for up to 9 arguments, but you can add as many as you like.
  • return values are not handled
  • it's not fast, but that's not the point

    This is a fairly specific piece of code that I wrote with some, but ultimately no particular purpose in mind, but I imagine there are people whom this might benefit a lot more. It was a fun exercise and a way practice template voodoo. There are a few macros in there as well to drastically cut down code bloat. They do occasionally affect readability a bit, but also improve it in different ways.

    One thing that might likely make sense as an extension would be an incremental tester, which replaces one argument with an ill-formed value at a time.

    PS - hmm, GD doesn't allow attaching header files, so I zipped it.

This took me a bunch of time for a number of reasons - I'm still very new to PhysX, there were a number of issues that sidetracked me and coming up with a reproducible test scenario is actually a lot harder than it may seem at first glance. The number one problem that I faced, however, was a lack of information, which, combined with a couple of rash misreadings of the PhysX API, resulted in a lot of time lost for naught.

I tried a number of approaches, but I think I finally have it sorted out the way I want using a semi-kinematic solution suggested here. I'm posting some notes in case someone finds them useful:

  • use [font='courier new']setLinearVelocity()/addForce()[/font] and [font='courier new']setAngularVelocity()/addTorque()[/font] with [font='courier new']PxForceMode::eVELOCITY_CHANGE [/font]to keep the actor in front of the player, facing away from them
  • be sure to set the angular/linear velocity to zero after each substep, since the solver can introduce unwanted movement, which needs to be specifically killed
  • getting rid of jitter is primarily a matter of enabling CCD and substepping. I ended up settling on 3 substeps.
  • leave max angular velocity at default, but disable damping on the carried object using [font='courier new']setAngularDamping(0)[/font]
  • [font=arial]disable gravity for the carried actor[/font]
  • the most important thing is to disable automatic rotation for the actor using [font='courier new']setMassSpaceInertiaTensor(PxVec3(0, 0, 0))[/font]. Annoyingly the name and documentation make this function sound a lot more fancy than it really is. In this particular case it's a substitute for disabling automatic rotation around specific axes. This functionality was apparently present under a very different guise in previous versions of PhysX
  • for the carried actor to be blocked by other dynamic actors, set its mass to something very small, as PhysX treats zero mass as infinite, effectively otherwise turning the dynamic body into a kind of kinematic actor, which pushes all other dynamic bodies out of its way. In particular, I'm calling [font='courier new']PxRigidBodyExt::setMassAndUpdateInertia(dynBody, PxReal(0.0001))[/font] when the object is picked up and restoring its mass when it is dropped. Glossing over this the first time I read the documentation on actor mass cost me dearly.
  • even with this in place, I'm still seeing jitter when doing some extreme forcing of the object. Eg, when the player stands next to a wall holding a box and turns into it, the box will start jittering as it is left behind the player's back (since the box is always facing away from the player, it needs to perform a considerable move and almost half a rotation (or depending on the direction in which the player is turning, more than half a rotation) within whatever number of substeps you are using). My solution is to drop the item when it goes behind the player's right axis. I suppose increasing the number of substeps might help, but even though I have almost no physics to speak of, the performance impact is not that trivial (that being said, I am in debug mode with PvD enabled using the full profile)
  • I am seeing no jitter or penetration when angular velocity is zero. Hence, when trying out different things, I considered switching the box's cuboid shape for a capsule, which doesn't need to be rotated. This might be a good option for a variety of reasons, the most notable of which is more natural sliding introduced by the capsule's round shape
  • I'm calculating the required angular velocity using atan2. Note that pushing the carried object still yields jitter, in particular when crossing over atan's -PI/+PI wraparound, so a special case fix is necessary:
    PxReal rotTar = math::atan2(vecTarget.z, vecTarget.x);PxReal rotCur = math::atan2(vecForward.z, vecForward.x);if(Sign(rotCur) != Sign(rotTar)){#define TAN_QUADRANT(x) (x < -math::Const::HalfPi) ? 1 : ((x > math::Const::HalfPi) ? 4 : ((x < 0) ? 2 : 3)) int32 quadrantRotCur = TAN_QUADRANT(rotCur); int32 quadrantRotTar = TAN_QUADRANT(rotTar); if(quadrantRotTar != quadrantRotCur) { if(quadrantRotCur == 1 && quadrantRotTar == 4) { rotCur += math::Const::TwoPi; } else if(quadrantRotCur == 4 && quadrantRotTar == 1) { rotTar += math::Const::TwoPi; } }}rotDelta = rotCur - rotTar;

    • as of right now the actor's collision with the CCT (character controller) is still wonky. It collides when the player walks into the actor, but seems to be ignored when the box needs to travel to the other side of the player. Ironically, while essentially downright broken, this behavior is very much desired in 3rd person mode, since the player can cause the CCT to flip by simply moving the mouse across its origin
Sign in to follow this  
Followers 0