Please check the zenilib commit log to see if it has been addressed since the most recent release. If it has, please pester me to package and release an update.
Always remember that a reproducable test case is worth a lot more than a bug report alone, but any report is better than nothing.
To the same place you extracted zenilib. Please see the page explaining how to Build a Game.
It is possible that you need to add paths for the Windows SDK. If you manually installed the Windows SDK, you may also need Include/Lib paths from
C:\Program Files (x86)\Microsoft SDKs\Windows\.
zenilib/directory if you haven't already.
zenilib/assets/* zenilib/dev/pc_/* zenilib/jni/application/*Note that some of these files may have been updated in the new version, and your existing files will need to be discarded or updated in order for zenilib to function. For example, 0.5.1.0 adds a sound alias to
On other platforms, the
zenilib/ directory essentially is the build of the game with extra stuff, like source code, included. If you're using the Xcode IDE, most of your buid is stored in a DerivedData directory (or at the location specified in your Xcode preferences).
The App bundle itself should appear at
zenilib/game_d.app for the Debug build, or
zenilib/game.app, regardless of whether you build with the command-line or with Xcode.
In the process of building the App bundle, your
assets/ directory is mirrored into the App bundle at
Contents/assets/. As a result, any files output by your game, including
stdout.txt will appear inside the App bundle rather than inside your
zenilib/ directory. Every time you recompile, the files in your App bundle will be reset.
Please read the last section of What's special about Mac OS X?.
cout is dumped to
cerr is dumped to
stderr.txt. In Windows and Linux, both files are found in the root of your
zenilib/ directory. In Mac OS X, both files are buried in your App bundle, as described in What's special about Mac OS X?
If you wish to output text you can see while in-game, outputting to the console might be more useful:
#ifndef NDEBUG get_Game().write_to_console("Hello World!"); #endif
<!-- Make sure you didn't add it inside of a comment block. -->
zenilib/directory on the desktop.
zenilib/directory. Replace all files when asked. If it does not ask to overwrite files, you have made an error. Examine the file structure of the example .zip and see how it corresponds to the existing
Heap Corruption often implies that Visual Studio needed to rebuild something, but wasn't smart enough to figure it out. Doing a clean+rebuild often solves that problem. You are most likely to encounter this error when exiting a
There is also a chance that you need to add a
virtual destructor to at least one of your base classes -- especially if derived classes contain instances of
After the initial bootstrap to your
Title_State or other
Gamestate_Base-derived type, subsequent transitions simply involve the use of
get_Game().push_state(...) optionally preceded by
get_Game().pop_state(). A popped
Gamestate (counting smart pointer), returned by
pop_state(), can be saved for later use if desired.
You could render your framerate, for example, with
get_Fonts()["title"].render_text("FPS: " + ulltoa(get_Game().get_fps()), Point2f(), Color()); Using an
ostringstream would be a good alternative to using functions like
ftoa, ... and
Text_Boxes are another option, but they would be overkill for most purposes.
After you're done rendering your scene in 2D or 3D, you can call
get_Video().set_2d(...) and render whatever HUD elements you like. Doing this even in 2D is a good idea because it keeps code simple and prevents a class of rendering artifact caused by floating point error. Calling
get_Video().clear_depth_buffer() can prevent 3D geometry rendered earlier from popping in front of your HUD.
You must approximate the geometry using a set of
Triangles centered around the center of the circle, or render a
Texture of the primitive.
Open the image file in the GIMP (or in GIMP Portable).
If you don't see a strange checkered background, you failed to create your image correctly (with an alpha channel), or you failed to export it correctly. Be sure not to flatten your image as you export it. And remember, always use .png, and always powers of 2 for width and height.
1 in textures.xml and use texture coordinates outside the range [0,1].
// In render() Quadrilateral<Vertex2f_Texture> quad( Vertex2f_Texture(Point2f(x , y ), Point2f(-1.0f, 0.0f)), Vertex2f_Texture(Point2f(x , y + h), Point2f(-1.0f, 2.0f)), Vertex2f_Texture(Point2f(x + w, y + h), Point2f( 3.0f, 2.0f)), Vertex2f_Texture(Point2f(x + w, y ), Point2f( 3.0f, 0.0f))); Material material("texture"); quad.lend_Material(&material); get_Video().render(quad);
Make sure the width and height of the image being loaded are both powers of 2. Make sure you disabled tiling of the particular texture in
zenilib/assets/config/textures.xml. Finally, be precise in your texture coordinates.
The only way to be absolutely sure is to use a fixed time step. So long as all changes to game state are made in
perform_logic, you can accumulate the amount of time that has passed, and whenever it gets above a certain threshold, subtract off that fixed amount of time and perform a game step.
However, this isn't optimal for smoothness or performance, generally speaking. It is usually good enough to multiply all changes in motion by the amount of time that has passed since the previous frame. (e.g.
position = time_step * velocity) The one caveat is that you probably need a bound on the size of a step to ensure stability, or you might just find yourself able to jump though a wall one time in a hundred.
F5, to run rather than debug your game.
zenilib/config/zenilib.xmlif your game is 2D, it won't make things look prettier in 2D anyway.
zenilib/config/zenilib.xml, it gives a slight performance boost on some systems.
Try storing Quadrilaterals and rendering them manually:
// Possibly in a constructor Vertex2f_Texture ul(Point2f(x , y ), Point2f(0.0f, 0.0f)); Vertex2f_Texture ll(Point2f(x , y + h), Point2f(0.0f, 1.0f)); Vertex2f_Texture lr(Point2f(x + w, y + h), Point2f(1.0f, 1.0f)); Vertex2f_Texture ur(Point2f(x + w, y ), Point2f(1.0f, 0.0f)); Material material("texture"); Quadrilateral<Vertex2f_Texture> quad(ul, ll, lr, ur); quad.fax_Material(&material); // In render() get_Video().render(quad);
If you are rendering a number of primitives using the same
Texture, relying on zenilib to automatically set and unset a
Material per primitive is inefficient. Furthermore, it sets a number of parameters which only matter when lighting is enabled. In the future,batch calls may be supported but, for now, you can get an order of magnitude performance improvement by setting a
Texture once, doing a number of rendering calls, and then unsetting the
Texture, as follows:
Quadrilateral<Vertex2f_Texture> quad(ul, ll, lr, ur); //quad.fax_Material(&material); get_Video().apply_Texture(get_Textures()["texture"]); get_Video().render(quad); //get_Video().render(quad2); //get_Video().render(quad3); get_Video().unapply_Texture();
Note that no
Material was ever given to the
Quadrilateral in this case, because the
Texture is being set manually at render time.
If that isn't good enough, and you have a number of primitives which are guaranteed to stay the same from frame to frame, it's time to learn how to use Vertex Buffer Objects (VBOs). class Vertex_Buffer allows you to insert Triangle and Quadrilateral objects. After you give it all the Triangles and Quadrilaterals that you want it to be able to render, the Vertex_Buffer will be ready to render. Note that there will be no performance boost if the number of triangles in a Vertex_Buffers is very small. Note also that building a Vertex_Buffer is expensive, and should not be done every frame. (Recreating a Vertex_Buffer every frame defeats the purpose of using one.)
// Possibly in a constructor Vertex_Buffer *vbo_ptr = new Vertex_Buffer; vbo_ptr->give_quadrilateral(quad_ptr); // this pointer is dead to you vbo_ptr->fax_quadrilateral(quad_ptr); // you can keep the pointer // In render() vbo_ptr->render();
Rendering a VBO is much faster than rendering a number of Triangles or Quadrilaterals individually. However, you can't change values within a VBO after it is created. So, if you want to move one around, the correct approach is to use Model/World matrix transformations.
Video &vr = get_Video(); vr.push_world_stack(); //vr.translate_scene(...); //vr.rotate_scene(...); //vr.scale_scene(...); vbo_ptr->render(); vr.pop_world_stack();
You don't. Once a Vertex_Buffer is rendered, it becomes fixed. You must delete the old one and make a new one if you need to change the contents. Note that it is possible to create vertex buffers which can be modified--I just don't provide this functionality.
Any Widget can be given a new
Text_Button tb can have its
Colors changed, for example, by
More drastic theming, for example switching to Textures, would require creating a new derived type of
Widget_Renderer, with a
render_to(...) function that knows how to render the desired type of
Widget. Note that type information is lost in the call to
render_to(...), and that
Widgets do not enforce that they receive only
Widget_Renderers that they know how to render them. It is up to you to ensure that your
render_to(...) function knows how to
dynamic_cast to the appropriate
Widget type, and to use the information within to render the Widget.
Zeni::play_sound(...) defined in
Sound_Source_Pool.h is the simplest way to play a one-shot sound effect, loaded using the
Sounds database. An alternative is to manually create a
Sound_Source and pass it a
Sounds manually. This is necessary only for looping sounds and for positional audio, in cases where it would be required to move a
Sound_Source before it finishes playing.
Music can be handled simply by the BGM functions
get_Sound().play_BGM(). Note that these functions are not tied to the
Sounds database, so a relative path is expected rather than an alias from
The Chronometer<Time> class is helpful.
// Possibly in a constructor m_seconds_passed = 0.0f; m_chrono.start(); // In perform_logic() const float seconds_passed = m_chrono.seconds(); const float time_step = seconds_passed - m_seconds_passed; m_seconds_passed = seconds_passed;
Additionally, the Time values from Timer can be used directly when convenient, or when you want to keep your values from being affected when the user pauses the game.
The first parameter of
get_Video().set_2d(...) is an
Point2fs. By default, the first is (0, 0) and the second is (screen_width, screen_height). However, you can them to arbitrary coordinates in your 2D world.
Alternatively, the scene transformations described in Rendering Triangles/Quadrilaterals is too slow! can have the same effect.
Pass in the upper left corner as the first parameter. For the next two parameters, pass in
Vector3f(width, 0.0f, 0.0f) and
Vector3f(0.0f, height, 0.0f). Finally, pass in
Vector3f(0.0f, 0.0f, 1.0f) for the fourth and final parameter. Without specifying a height, the Parallelepipeds are degenerate and will give erroneous intersection/distance values. Of course, if your Parallelepipeds are not axis-aligned, you will have to do some extra work manipulating the parameters.
User-specific files should, ideally, be stored in files saved the directory path returned from
get_File_Ops().get_appdata_path(). This directory is likely to be writable in the case that your game has been installed. Writing to relative paths within the
assets/ directory is safe so long as installation is not intended.
Distribute a zenilib Game.
./zenilib/multi-clean_sh.bat. If you want to clear out the binaries for other operating systems, you can run it with the option
--build=all, but you will have to rebuild your project afterwards, as it will delete the files for your operating system also.
zenilib/directory can grow by hundreds of megabytes. This reduces the default
Remember that as the zenilib is licensed to you under LGPL v3.0, you are legally obligated to provide a copy of zenilib, and the source files for the DLLs I provide to you, or to provide a written offer to provide them at no cost upon request. Again, if submitting to a class, the instructor probably wants zenilib and your source code intact, but a written offer is probably enough for the source for the accompanying DLLs.
Don't build from a network drive. If you wish to store your files on a network drive, relocate the build directory with
multi-premake_sh.bat --dir. See Build a zenilib Game for details.
It is also possible that at least one of your files probably has a file modification time that is in the future. This can happen if you edit files on different computers that disagree about how to keep time. The solution is to update the modification times of all your header and source files. (This can be done by opening the offending files, modifying them, and resaving them, or with the help of a command-line tool such as
Mto open the Material Editor.
Materialmenu, click on
Change Material/Map Type....
Standardin the window that opens.
Pick Material from Objectbutton (looks like a dropper).
Diffusecolors to white.
Specularcolor to the approximate color of the object (or white if it is plastic).
Mapstab and check the box next to
Noneon the same line.
Bitmapin the window that opens and click
Coordinatestab that appears after the Texture is set, uncheck
Use Real-World Scale.
Vto be 1.0 by default.
Real-World Map Sizefor the selected mesh. (You can uncheck this in the
Createtab as well, but it will apply only to newly created meshes.)
Assign Material to Selectionbutton (looks like
Preserve 3ds Max's Texture Coordinates.
Bitmap Parameteryou added to your mesh to textures.xml. To ensure that you get it right, I recommend reimporting the .3ds file and seeing what value is inside. It won't match what was set initially. Of course, if you get it wrong, zenilib will Error and tell you what you need to add in
3DS files seem to refer to textures by all caps file names. (At least this is the result when exporting from 3ds Max.) zenilib blindly looks in the
Textures database to find the corresponding textures.
e.g. Applying car.jpg to part of a model in 3ds Max will result in a reference to CAR.JPG in the 3DS file. CAR.JPG can refer to whatever you like in the
Textures database. (It could be an image loaded from car.png. It could just as easily be a
If your program throws an
Error when you try to render the
Model, you can check
zenilib/stderr.txt for an error message telling you what
Texture it was looking for.