This is one (big) bullet shot which went through the triangle out to the other side. It supports clipping against static geometry and fades at an angle.
Front side:
Back Side:
This is one (big) bullet shot which went through the triangle out to the other side. It supports clipping against static geometry and fades at an angle.
Front side:
Back Side:
I’ve been using CEGUI for a couple of weeks now. I’ve been trying to get a string to show up, with word wrap, for 5 days. Not actively, but on and off I’ve been pursuing this topic for that long.
My difficulty lies in that I don’t need to use their window system, with selectable text, etc. And I don’t want to define my text in an xml file, the preferred way to setup your interface. I simply want to do the equivalent of printing “Hello World” purely from code, including word wrap.
My first approach was to use the Font class. It seemed pretty good – there is a function to tell you how long a string was and how many lines it would span. I didn’t see a function to tell you where exactly the string was split but it’s a good start. The problem was that it didn’t work. So I asked about this on the forum. A day later I was essentially told not to use that class directly. It didn’t bother me too much, because I had a hunch of the technical reasons why this might be, but user classes should have clearly been delineated from internal classes and marked as such. As an aside, in RakNet all classes are independently functional and are documented as to their use, not just with Doxygen.
Anyway, to display text, you have to create a window and set the text for that window. How do you do this? There’s a Window class, a StaticText class, a DefaultWindow type, and a GUISheet class. GUISheet is the one to use although StaticText is the one you would think to use, with functions to set the text color, formatting, and other things. However, StaticText contains abstract functions, meaning you can’t use it directly, as I found out after I went to the trouble of trying to use it and then compiling. It turns out this is a class for internal use only. Arguably this was my fault for not looking closely enough to see that there were pure virtual functions. This is the same problem as before though – it should have been marked which classes were for the user and which not.
It’s definitely not obvious that GUISheet is the class you would use to display text. This is doubly the case because GUISheet does not contain functions related to text. It turns out the way it supports text operation is through a setProperty function which takes a string for the action to be performed and a string for the parameter. For example. “HorizFormatting” followed by “LeftAligned” (or something similar). I’m not sure how you are supposed to know this. In order to determine what properties were supported I had to search the source code for “: public Property”
Using strings as the native interface to set properties is a design mistake because it adds an unnecessary layer of abstraction between the user and the intended functionality. Rather than simply passing native types around (such as numbers) I have to convert them to strings. When I want to get the native type back from the string I have to do a reverse conversion, which I’m not sure is even supported. The reason they did this was to more easily support scripting. However, the proper way to architect this would have been to provide a translation layer from script strings to native types, such as numbers, with the reverse operation supported as well, rather than taking strings directly and hiding the native types.
CEGUI is very powerful if you stick to the mechanisms they give you. With almost no effort you can add tables, scroll bars, buttons, input windows, and other widgets. However, these widgets do not provide everything you might need. For example, my current difficulty comes from the fact that I’m trying to fade out and then delete text over time, a function not natively provided. It seems like cracks in the armor start to show through in those instances.
For API usability I’d give CEGUI a C-. It’s well documented at the level of Doxygen. They use Doxygen much better than RakNet does. Depreciated and non-user classes are not documented as such that I was able to see. The newer features seem to have worse architecture than the older features. However, the availability of the source code is a huge plus: it makes what would be otherwise insurmountable problems minor inconveniences.
For functionality I’d give it a B+. It does a lot and covers most of the common features. My favorite feature is input parsing, so it will natively understand double clicks. It also supports copy/paste, text highlighting, tables, and scroll bars. The GUI editor is a major benefit.
Here’s the problem:
Often in games when text shows up on the screen it doesn’t just pop in and pop out. Instead, it will fade in, display for some time, and fade out.
CEGUI (my GUI library) doesn’t support this. So I have to write something myself.
Solution:
Obviously the solution is to modify the alpha of the string.
My question for you:
“What is the best way to architect this?” Composition? Inheritance? Modify the string class? Write a string manager? Something else? This is non-trivial.
Think about your answer, then scroll down
.
.
.
.
.
If you’ve read my previous posts, the best architecture has small units of functionality that do the minimum possible while accomplishing the task at hand.
Classes which are well designed
A. Have loose coupling, which means that they have generic inputs and outputs.
B. Have tight cohesion, which mean they don’t rely on other systems to do the work they are responsible for.
Think again what you would do, then scroll down for what I did:
.
.
.
.
.
Header file:
#ifndef __FADE_CONTROLLER_H
#define __FADE_CONTROLLER_H
// Assumes the object being faded has an alpha.
// This handles the math of fading in and out.
// Just have an instance of this class composited with whatever you want faded. Then multiply GetAlpha by the alpha of the object.
// Call Update with the elapsed time since the last call to Update
// You can freely change the member variables of the class
struct FadeController
{
FadeController();
~FadeController();
float GetAlpha(void) const;
bool IsExpired(void) const;
void Update(unsigned elapsedTime);
// If fadeInTime < elapsedTime, the percentile elapsed will be used as alpha unsigned int fadeInTimeMS; // If lifeTime!=0 && lifeTime < elapsedTime then this string will be removed from AlphaTextList unsigned int lifeTimeMS; // Total MS elapsed unsigned int elapsedTimeMS; // Will calculate alpha as the remaining time unsigned int fadeOutTimeMS; }; #endif
Source file:
#include "FadeController.h"
FadeController::FadeController()
{
}
FadeController::~FadeController()
{
}
float FadeController::GetAlpha(void) const
{
if (elapsedTimeMS < fadeInTimeMS)
return (float) elapsedTimeMS / (float) fadeInTimeMS;
else if (lifeTimeMS!=0)
{
if (elapsedTimeMS < fadeOutTimeMS)
return 1.0f;
else if (elapsedTimeMS < lifeTimeMS)
return (float) (lifeTimeMS-fadeOutTimeMS) /(float) (lifeTimeMS-elapsedTimeMS);
return 0.0f;
}
else
return 1.0f;
}
void FadeController::Update(unsigned elapsedTime)
{
elapsedTimeMS+=elapsedTime;
}
bool FadeController::IsExpired(void) const
{
return lifeTimeMS!=0 && elapsedTimeMS > lifeTimeMS;
}
Seems very simple right? But consider that there are NO external dependencies. This will link fast, can work with any game, and in fact overengineers the problem beecause it can work with things other than strings. For example, shrapnel could fade out too.
Just composite this class with whatever you want faded and hook it in with two lines of code.
If you have come up with a better solution, post it in the comments.
I noticed that Visual Studio 2005 will frivolously ask you if you want to build out of date projects. For example, if I change a file in one project, build that project and its dependencies, and then try to run it will tell me that unrelated projects are out of date. Eventually I disabled that warning. Now I get a problem where, after building a project, it won’t take my changes or will tell me the source file is wrong.
So it seems like the warning was right and the real problem is even though you build your project sometimes you need to build it twice for no reason.
As in my last post I’m working on the GUI system for my engine. I’m using CEGUI and positively suprised me. I went to their forums and gave my requirements, which are non-trivial:
Scrolling chat window that splits text, with a per-line prefix
Line, bar graphs
Tables
Console input window.
While they couldn’t do everything I wanted (graphs were for a later version) everything else they claimed they could do directly and it was in their sample already.
I’m investigating now. If this is the case then it’ll save me a lot of time.
Ogre 3D doesn’t have native functionality to display debug text. So I spent a half day adding a layer to their overlay system to make it easier to use. You can now do the exact same thing with half the lines of code, no loss of functionality, and with greater safety, including automatic memory handling. This is essentially free in terms of speed.
I now got text showing up and fading out over time. But then I found out Ogre3D also doesn’t have the ability to do automatic line wraps… Or positioning of multiple lines of text.
It looks like the way to go if you want debug text in ogre 3D is to use CEGUI. It already handles line wrapping and I’m pretty sure it also handles multiple lines of text. If it doesn’t do what I need I can write my own text positioning system in a day or so.
I’m at the point where I need to design the high level architecture for my game. What classes will there be? What operations will they perform? How will they relate to other classes?
The problem is that my answer is usually “I don’t know” or “I don’t know very well.” I can roughly say that “It’s an MMOG game so it will have a client and a server.” That’s true, except it’s not very useful information. For example, it’s wrong to say I have “a server” because for scalablility I plan to use server clustering. And what is a client? There is a game client for sure, but there are or may be remote administration clients or logging clients as well.
So I can’t really design the whole game architecture on paper right out. The human mind just can’t explore every possible detail in advance. So how about extreme programming? In XP you have a shipping product pretty much every week. And you build on that project from week to week. This isn’t a bad approach and it’s certainly better for motivation. At the same time you can’t totally ignore high level goals. If I were to do that using a simple minded approach I would hardcode it to have just one server, which means eventually I will have to go back and change code I’ve already written, wasting time. Changing old work that already works tends to be harder and more buggy than writing work the right way to begin with too.
So what’s the solution? For me, it’s an extension of the design principle I already use, which is “Don’t make decisions that have exceptions.” In this case it’s more like “Don’t make decisions I’m not absolutely sure of.” That’s a good starting point because it gives me code to work from. Some things I’m sure of:
So this actually gives me something to work from right away. I can create 3 classes, I can design those classes for code exclusion, and I can write a chatting system in the game.
Based on those modules I can think more about the problem and design other modules.
The positive results of this system are first that I don’t get paralyzed trying to think of what to do. Second, I’m unlikely to waste or redo work. Third, and most importantly, it forces me to carefully think about my systems and not gloss over details.
This is what works for me. Feel free to post your own ideas.
Combines Ogre3D, fmod, RakNet, OIS, and Crazy Eddie’s GUI system. OIS (the input system) and Crazy Eddie’s GUI system do not work together very well and OIS isn’t very well designed so I wrote an input manager which fixes that problem.
The majority of my work went into setting up the projects so they work together nicely. All the libraries go to one place, the output to another place, and so forth. There are includes and dependencies all over the place so I subdivided them among projects.
So now it’s tremendously easy to create a new game, with access to the functionality of all those systems, without having to write all kinds of setup crap.
One big plus is Ogre3D supports lots of content creation tools so the particles were made by me, as well as the GUI layout.
Here’s a screenshot which shows all the systems except RakNet in action. Everything you see was done in 231 lines of game code, which includes winmain()
*** EDIT ***
You can get it here: RakEngine .1
One thing I’ve learned from doing RakNet and am trying to do in RakEngine is to follow good systems design, which can be summarized as
1. Preserve generality
2. Preserve relevant information
This can be summarized in one sentence: Don’t make decisions that have exceptions – either in regard to processing or information hiding. Almost every time there is a case where someone says “This code is all over the place” it is because this rule was violated.
An example of this is a class that calculates and stores AI paths. Lets say you make a decision to limit updates to every 500 milliseconds. During later testing you find that the AI doesn’t respond fast enough when getting hit, and in that particular scenario you need to update the path right away. So now you have an exception to your rule of 500 milliseconds, which means it was a mistake to make that rule to begin with. Except that now when the AI gets hit a lot, you don’t want to recalculate the path again, so you have to add a special flag to disable this. Except for a third case when a path blocker comes up. And so forth. Now your code which simply finds AI paths has special flags for things it shouldn’t know about (blockers and getting hit).
It’s much harder to design systems that maintain generality as opposed to systems that are specific. You usually have less features. One way to tackle this is to define primitives, where a primitive is the minimum unit of functionality and information that anyone could ever care about. Then build up systems composed of those primitives. A good example of a primitive is the sin() function. A sin function is an expansion series. There is a certain amount of internal data going on there such as the nth term and how far the series is expanded. But you don’t care about that. So it’s not exposed and the sin function works well, without people having to rewrite it. Suppose instead the sin function calculated both the sin and the cosine at the same time, at a cheaper cost than calculating both separately but more expensive than individually. That would be a good thing, except in cases where you don’t care about the cosine. So you no longer have a primitive, which is why that is not done.
Sometimes designers think something is a primitive and it is not. For example, bytes. In network code I deal with bits all the time. The bit is actually the minimum computing unit. Because of the abstraction of bytes I had to write a bitstream class with a huge set of complicated functions to read and write bits. So defining bytes as the minimum memory unit was a bad abstraction. A better abstraction would have been to make a bit a native type, and to use that in composition, where you have a bit8 as another type, and so on.
The native C/C++ types are also bad abstractions. They try to hide the underlying type by representing the most efficient form for the compiler. So an int is the type you should usually use for numbers, a char is used for characters, and so on. But in practice a great deal of the time you don’t care about that – you care about how many bytes are used. So you end up with almost every library doing something like
typedef unsigned char UCHAR;
And when you use another library, you can’t just use UCHAR. You have to look at what type that actually was, and match it up with your own type. So people have proposed to do something like
typedef unsigned char u8;
typedef signed int s32;
And so forth. This is good, because it is more of a primitive than int or char. You are undoing the bad design work which shouldn’t have been done to begin with.
So a good rule of thumb when designing systems is “Is there any case where anyone would want to process something differently than I just wrote?” And “Is there any case where anyone would want to view this data I am setting as protected or private?” If yes, you need to think carefully about that because you will have to write exceptions, which multiplies the complexity of your system. It might be better to go to a lower level of granularity and compose the system instead.
I have 4 systems running now. I need to integrate RakVoice with fmod next.
I would have had all this done yesterday except that I spent a huge amount of time trying to get Ogre to work from source, instead of using their framework. Eventually I gave up on that idea.
I thought it would be a simple matter to get the GUI on the screen but it seems like that’s one of the hardest things to do of all. I think I’m going to have to write a wrapper for Crazy Eddie’s GUI system because there’s a ton of code involved to do things that should have been automated. For example, mouseover and mouseoff highlights, setting callbacks for various operations, and such. You have to load the textures yourself even. With as much as you have to do yourself I’m not sure what functionality Crazy Eddie’s system even provides.
The system I had in Irrlicht was much better. It was one line of code to add a GUI element and it did everything else for you. Rather than having to create a class and then setting and writing callbacks for each button I just handled an event in a switch / case.