Friday, December 2

Unrealscript Overview Part Six : Actor

We don't actually place bubbles in the world. We use an object placed in the level as a point to spawn bubbles from, particle - system style. BubbleSpawner is this object. Note that it is marked as a placable so that it can be placed in the editor. The variables qualified by the (Bubble) suffix in their declaration appear as editiable properties in-game. This is an important mechanism to allow designers and artists to tweak the game without programmer input, letting you get on with core logic implementation.



In PostBeginPlay we vary the spawnCounter a bit, to be sure that mutliple emitters aren't in phase. Tick() just counts down using delta time, waiting until the time calsle to spawn a bubble. When it comes, the Spawn() function is called to randomly create a new BubblePawn somewhere randomly around the general vicinity of the spawner. That's all it takes to dynamically create a new in-world actor - the Pawn itself should take care of spawning it's controller.


class BubbleSpawner extends Actor placeable;

var(Bubble) float spawnRange;
var float spawnCountDown;
var(Bubble) float spawnInterval;

event PostBeginPlay()
{
spawnCountDown = spawnInterval * 2.5;
super.PostBeginPlay();
SetHidden(True);
}

event Tick(float DeltaTime)
{
local Vector SpawnLocation;
spawnCountDown -= DeltaTime;
if (spawnCountDown < 0)
{
SpawnLocation = self.Location;
SpawnLocation.X += -spawnRange + FRand() * (spawnRange * 2.0);
SpawnLocation.Y += -spawnRange + FRand() * (spawnRange * 2.0);
spawnCountDown = spawnInterval;
Spawn(class'LavaLamp.BubblePawn', Self,, SpawnLocation );
}
}

defaultproperties
{
spawnInterval = 0.75
spawnRange = 64.0
}

Unrealscript Overview Part Five : Player Controller and Camera

The PlayerController, derived from GamePlayerController, is the Player counterpart of the NPC GameAIController. The Player is put into the walking state by default. We co straight from here to the Spectating state, suitable for a free-flying camera. The states are implemented in GamePlayerController and it's base classes. The UpdateRotation() method is called from the base controllers Tick(). It's job is to transfer changes in PlayerInput and translate them into actual motion that sets the position and orientation of the player. ProcessMove() simply updates the Pawn's acceleration and leaves it to the base clases to update the Pawns velocity and position accordingly.

Player Controller


class LavaLampPlayerController extends GamePlayerController;

defaultproperties 
{
 CameraClass=class'LavaLamp.LavaLampPlayerCamera'
}


simulated event PostBeginPlay()
{
 super.PostBeginPlay();
}



/*
* The default state for the player controller
*/

state PlayerWalking
{
 event BeginState(Name PreviousStateName)
 {
  GotoState('Spectating');
 }
}


/*
* The default state for the lava lamp demo
*/

state PlayerSpectating
{
 event BeginState(Name PreviousStateName)
 {
  bCollideWorld = false;
 }

 function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
 {
  if( Pawn == None )
  {
   return;
  }

  if (Role == ROLE_Authority)
  {
   // Update ViewPitch for remote clients
   Pawn.SetRemoteViewPitch( Rotation.Pitch );
  }

  Pawn.Acceleration = NewAccel;
 }



 function UpdateRotation( float DeltaTime )
 {
  local Rotator   DeltaRot, newRotation, ViewRotation;

  ViewRotation = Rotation;
  if (Pawn!=none)
  {
   Pawn.SetDesiredRotation(ViewRotation);
  }


  // Calculate Delta to be applied on ViewRotation
  DeltaRot.Yaw      = PlayerInput.aTurn;
  DeltaRot.Pitch    = PlayerInput.aLookUp;

  ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot );

  SetRotation(ViewRotation);
  NewRotation = ViewRotation;
  NewRotation.Roll = Rotation.Roll;
  if ( Pawn != None ) {
   Pawn.FaceRotation(NewRotation, deltatime);
  }
 }
}

Player Camera


class LavaLampPlayerCamera extends Camera;


function UpdateViewTarget(out TViewTarget OutVT, float DeltaTime)
{
 local CameraActor   CamActor;
 local Pawn     TPawn;

 // Don't update outgoing viewtarget during an interpolation 

 if( PendingViewTarget.Target != None && OutVT == ViewTarget && BlendParams.bLockOutgoing )
 {
  return;
 }

 // Default FOV on viewtarget
 OutVT.POV.FOV = DefaultFOV;

 // Viewing through a camera actor.
 CamActor = CameraActor(OutVT.Target);

 if( CamActor != None )
 {
  CamActor.GetCameraView(DeltaTime, OutVT.POV);

  // Grab aspect ratio from the CameraActor.
  bConstrainAspectRatio   = bConstrainAspectRatio || CamActor.bConstrainAspectRatio;
  OutVT.AspectRatio   = CamActor.AspectRatio;

  // See if the CameraActor wants to override the PostProcess settings used.
  CamOverridePostProcessAlpha = CamActor.CamOverridePostProcessAlpha;
  CamPostProcessSettings = CamActor.CamOverridePostProcess;
 }
 else
 {
  TPawn = Pawn(OutVT.Target);

  // Give Pawn Viewtarget a chance to dictate the camera position.
  // If Pawn doesn't override the camera view, then we proceed with our own defaults
  if( TPawn == None || !TPawn.CalcCamera(DeltaTime, OutVT.POV.Location, OutVT.POV.Rotation, OutVT.POV.FOV) )
  {   
   // for this demo, we just follow the player controller
   OutVT.POV.Rotation = PCOwner.Rotation;              
   OutVT.POV.Location = PCOwner.Location;
  }
 }


 // Apply camera modifiers at the end (view shakes for example)
 ApplyCameraModifiers(DeltaTime, OutVT.POV);
}


defaultproperties
{

}



Unrealscript Overview Part Four : Player Pawn

Our "Player" class is much less interesting than you might expect, as we are not implementing a player as such but a simple free camera. We override GetBaseAimRotation to control the viewpoint of the Pawn. We ensure that the Pawn can have it's view updated fully by the rotation pitch, roll and yaw angles. If we wanted to limit the viewing direction we would hard code tempRot.pitch = 0 in this routine to disable looking up and down for instance. BecomeViewTarget is called when a camera focuses on the player, and is our chance to block the request. Here, we allow it. As there is no mesh associated with our "Player" the light enviroment is redundant.

class LavaLampPawn extends GamePawn;



var DynamicLightEnvironmentComponent LightEnvironment;


//override to make player mesh visible by default

simulated event BecomeViewTarget( PlayerController PC )
{
 Super.BecomeViewTarget(PC);
}



simulated singular event Rotator GetBaseAimRotation()
{
 local Rotator tempRot;
 tempRot = Rotation;
 SetRotation(tempRot);
 return tempRot;
}



defaultproperties
{
 bCanWalk = true
 bCanSwim = false
 bCanFly  = false
 bCanClimbLadders = false
 bWorldGeometry = true
 bCollideActors = false
 bCollideWorld  = false
 bBlockActors   = false
 Begin Object Class=DynamicLightEnvironmentComponent Name=MyLightEnvironment
  bSynthesizeSHLight=TRUE
  bIsCharacterLightEnvironment=TRUE
  bUseBooleanEnvironmentShadowing=FALSE
 End Object

 Components.Add(MyLightEnvironment)
 LightEnvironment=MyLightEnvironment
}

Unrealscript Overview Part Three : NPC Controller

However, a pawn is not enough to give you a full NPC. A controller is also needed. Think of this as a model - view - controller set up. The view is handled by the rendering engine iteslf, the model is the unrealscript code for the pawn, and the actual controller is the controller class associated with the Pawn itself. Nearly all NPC or player pawns will have a controller associated with them. In the case of NPCs this will derive from AIController, which derives from GameController. These base classes define some logic for simple pathfollowing which may or may not be useful. In our example, the BubbleController does not use this derived class code.


AI Controllers usually are implemented as State machines. Unrealscript has a state construct that makes this easy. Code enclosed in a named state block is executed only when the object enters that state. The controller has an explicit method for switching state called GotoState(). As this is a simple example I have not overridden any methods inside the state block, but have written short fragments of code that can be executed latentley. Latent functions are Unrealscript speak for functions that run in the background. There are a small group of these available to you (grepping through the Controller, Actor and Pawn classes should reaval them), mostly related to Pawn movmement. Here, the rising state sets the Pawn velocity when it is etered, and then periodically checks to see if the bubble has risen too far. When it has, it enters the Popping state, wherein it stands still for a second, and then is destroyed.


The Destroy() function brings us to another point. Objects derived from actor have a special property in that they are spawned rather than created with the new() operator. In our BubblePawn class we called a base method called SpawnDefaultController(), and set ControllerClass to class'LavaLamp.BubbleController'. SpawnDefaultController will then create an instance of that controller class, and ensure that the controller Poseses (ie controls/owns) the Pawn by calling Posses. It is perfectly theoretially possible to pass a pawn around different controllers to implement different behaviours - although the state mechanism makes this less useful than it might sound.

class BubbleController extends AIController;



var float RisingDistance;

var float ExtraVelocity;



simulated Event SetInitialState() 

{

 ExtraVelocity = FRand() * 1.25;

 bScriptInitialized = true;

 GotoState('Rising');

}



state Rising

{

Begin: 

 Pawn.Velocity = vect(0.0, 0.0, 0.85);

 Pawn.Velocity.Z += ExtraVelocity;

RisingLoop:

 if (Pawn.Location.Z  > RisingDistance)  {

  StopLatentExecution();

  GotoState('Popping');

 }

 Sleep(1);

 goto 'RisingLoop';

}



state Popping

{

Begin:

 Pawn.Velocity = vect(0.0, 0.0, 0.0);

PoppingLoop:

 Sleep(1);

 Pawn.Destroy();

 Pawn = None;

 Destroy();

 goto 'PoppingLoop';

}





defaultproperties

{

 RisingDistance = 2048.0

}

Unrealscript Overview Part Two : NPC Pawn

The second class to look at is Pawn. The Pawn class extends the Object and Actor class and is responsible for most in-game entities. PostBeginPlay() is the method that is called when all engine-side initialisation of an actor is complete, so we usually do setup in here. In the case of the player, this means also spawning a default controller.


In BubblePawn, we are also initalising a material instance, an instance of a Material created with the Material Editor and given some named parameters within that editor. Creating a Material Instance lets us vary those parameters on a per-instance basis without creating a whole new material. This is what this looks like in the content browser.

..and in the Material Editor.



Tick() is the routine called every time the game state is updated. Overriding it enables you to completely alter the behavior of a given Pawn. In our case we are simply moving the bubble upward and varying it's scale to give a throbbing effect.


We are using dynamic lighting in our environment and thus we define properties for dynamic lighting of the object in our default properties. To aid speed we also turn off as many collision checks as possible. The static mesh component that we add is the mesh drawn by the renderer for this Pawn. The components are simply entries in a dynamic array of components and may be created or destroyed dynamically. The sprite removal is to simply remove a default placeholder sprite automatically added by the system, as this is a placeable and can actually be placed in the editor in the scene.






class BubblePawn extends Pawn placeable;



var float   accumulatedTime;

var StaticMeshComponent BubbleMesh;

var MaterialInstanceConstant MatInst;



simulated event PostBeginPlay()

{

 local LinearColor specularColor;



 accumulatedTime = FRand() * 180.0;

 super.PostBeginPlay();

 `log("spawning bubble ... ");

 SpawnDefaultController();

 MatInst = new(None) Class'MaterialInstanceConstant';

 MatInst.SetParent(BubbleMesh.GetMaterial(0)); 

 BubbleMesh.SetMaterial(0, MatInst);

 MatInst.SetScalarParameterValue('LavaEmissiveMultiplier', FRand());

 MatInst.SetScalarParameterValue('LavaSpecularPower', FRand() * 16.0); 

 specularColor = makeLinearColor(FRand(), FRand(), FRand(), 1.0);

 MatInst.SetVectorParameterValue('LavalSpecularColor', specularColor);

}



event Tick(float DeltaTime) 

{

 accumulatedTime += DeltaTime;

 SetDrawScale( 1.0 + 0.75 * Sin(accumulatedTime) );

 Move(self.Velocity);

 super.Tick(DeltaTime);

}





defaultproperties

{

 WalkingPhysics=Phys_NONE

 bCollideActors=false

 bCollideWorld=false

 DrawScale3D=(X=32.0,Y=32.0,Z=32.0)

 bNoEncroachCheck = true

 bIgnoreEncroachers = true

 ControllerClass=class'LavaLamp.BubbleController'

Begin Object Class=DynamicLightEnvironmentComponent Name=BubbleLightEnvironment

 ModShadowFadeoutTime=0.25

 MinTimeBetweenFullUpdates=0.2

 AmbientGlow=(R=.25,G=.25,B=.25,A=1)

 AmbientShadowColor=(R=0.15,G=0.15,B=0.15)

 LightShadowMode=LightShadow_ModulateBetter

 ShadowFilterQuality=SFQ_High

 bSynthesizeSHLight=TRUE

End Object

Components.Add(BubbleLightEnvironment)

Begin Object Class=StaticMeshComponent Name=InitialMesh

 StaticMesh=StaticMesh'Lava.Meshes.Bubble'

 bOwnerNoSee=false

 LightEnvironment=BubbleLightEnvironment;

 BlockRigidBody=false

 CollideActors=false

 BlockZeroExtent=false

 BlockNonZeroExtent=false

End Object

Components.Add(InitialMesh);

Components.Remove(Sprite);

BubbleMesh = InitialMesh

name='Bubble';

}

Dumping Blender Scenes as Lua


Following on to the previous post where I dumped blender scenes as Lisp, I've also written a modified version which is more usual for mainstream use that dumps the entire active object as an enormous Lua literal. I think this would be more useful for mainstream use as the Lua could then be compiled to binary and loaded into a tool, which could pick out and use the useful bits on a per-project basis. My next project is to investigate if such a thing is also possible with FBX & Python - again for more mainstream use.

Once more, here is the source Luke - use it :-)



Tuesday, November 15

Implementing Lisp in C++

I've been spending some time implementing a small Lisp for embedding and bootstrapping in C++, somewhat in the spirit of Scheme from Scratch.

The brief I've given myself is a bit contradictory in that I'm eschewing the use of virtual functions (as they make linking C to C++ more complicated) but, for now I'm still allowing boost as a dependency.

It turns out that Boost is highly useful for implementing polymorphic objects, since it contains variant - which can be used instead of the usual tagged unions.

To implement the basic Lisp Object I've created a template that implements the properties of the lisp object (copyable, assignable, and freely convertible to a base C type) and created a boost variant of all the different specialisations of this template which is the base class of our object.

This template should save writing boatloads of boilerplate code when contrasted with C and it should be much more easy to plug in new types. This has to be a win over C. The other big advantage (as noted before when I wrote the simpler Lisp) is that shared_ptr gives reference counted garbage collection more - or - less for free.

As usual, here's the code.

Thursday, October 20

Exporting Lisp From Blender

Writing a custom scene exporter for Blender turns out to be ridiculously easy compared to just about every other 3d package I've come across, mainly due to the power of Python introspection. This Python script shows how easy it is to introspect over a Blender Scene. After some fiddling I ended up with the Python script at the bottom of the post, which dumps the entire scene as an S-Expression for a Lisp instance to read.
My Lisp-based export problems are over!

Wednesday, September 7

Things that freak me out about Clojure

Coming from a Common Lisp/C++ background to Clojure is kind of .. interesting... there are several things that have made me take pause and wonder if Common Lisp is doing things the right way.


* Maps and Sets are functions. You can apply a value to a set and it returns that value if it's a member, nil if it's not. Similarly with maps. Apply a value to a map and it either returns nil, or the value the value maps to in the map. Seems strange at first but it allows some neat filtering tricks

* Keywords are functions. Keywords evaluate to themselves, just like in CL. Unlike in CL they are also functions so (:keyword map) actually tests map for membership of :keyword and returns the value mapped to the keyword. Again, this is handy.

* -> : This macro flattens out nested function calls so (-> fn1 fn2 fn3) is (fn1 (fn2 (fn3))). I really dislike this: I can see how coming from Java it would seemingly make code easier to read, and reduce syntatic noise, but I find the parens useful as a guide for indentation. This might be down to taste.

* Iteration is totally different. There's nothing like the LOOP or ITERATE. There is a while and a loop/recur, but the sequence handling is sufficently powerful that this is a fallback. It forces me to think differently and this is definitely a good thing.

* Probably more to come, but meanwhile I'll keep in mind these rules for writing Clojure.

Saturday, August 13

Code Kata For Game Developers


I've spent some time wondering what a set of Code Katas for game developers would look like. I'm thinking mostly in the context of learning a new language. Here's the list I have so far.

  • Mandelbrot set plotting
  • Simple raytracer.
  • Breakout clone.
  • Simple MUD.

Each kata would conentrate on different aspects of coding. The first, the Mandelbrot set is an old standard. Once it's done, you will know the mechanics of creating a 2d display, drawing on it, and getting decent performance out of simple numeric computation.

The raytracer, our second kata, takes us through the basics of 3d math and rendering, without actually requiring interactivity and will allow us to deploy most of the maths code needed to support 3d games on the platform. The two games suggested as exercies are Breakout and a simple MUD.

Breakout is a 2D graphical game, which would build on what was learnt performing the Mandelbrot set kata, and in addition require realtime interactivity and input, something not covered by the previous two katas, and also be a "proper" game with an initalisation phase, along with an update and render loop, giving us a framework to build our actual games around.

The final kata, the MUD concentrates on areas neglected until now: networking and text processing. These are important aspects of any platform and multi-player play. It should have enough game logic to support NPCs, and containers at the very least, but can be elaborated indefinitely..

I think these four kata would leave you in a very good position to go on to implement games on the platform you were exploring.

I'm wondering if there should be a fifth kata? I don't want this to become an exhaustive list - it needs to be a set of exercises that should take little more than a day each. One working week seems a reasonable time for a professional game developer to explore a new platform. What would he/she do on the fifth day. Any suggestions?


Wednesday, June 1

Unrealscript Overview Part One: The Game Info Class

A lot of Unrealscript source code ships with Unreal, and it's hard to know where to begin. This tutorial is to get you started and point out the major classes to extend, and methods to override. Its intended for programmers who already know one decent langauge like JavaScript, C++ or Java, to let them quickly locate key classes and methods and show how they fit together. The example centres round a simple level full of bubbles that keep continouosly spawning, with a a material, size and speed that varies per bubble, as in this video 
The map itself is very simple, and looks like this: It contains a BSP floor, a Light and a Player Controller. This is the minimum possible playable map - as we are more interested in code in this example.




The most central one is probably GameInfo. So, lets start there. GameInfo is the class that defines the Game rules and creates, tracks and destroys objects in the game world itself, and controls the game flow, as outlined on UDN





The very basic version of GameInfo needs to know only two things - the class which is used to implement the player Pawn and Controller. This is added in a defaultproperties section that usually comes after any code and is used to initialise variables at instance creation time. The PostLogin() is called by the engine after the player has been spawned and is meant to call StartMatch(), StartHumans(), RestartPlayer(), and SpawnDefaultPawnFor() in order to run the game. We override PostLogin() in our demo as we are implementing a demo and not a game, and do not want death and restart logic to apply to the player.

Monday, May 16

Emas and the Unreal Development Kit Workflow

I've been spending some more time with the Unreal Development Kit, as it seems to be trending in the world of games development, and partly to keep my hand in. I've been developing an Emacs based workflow and I thought I'd just quickly blog about it, in case my Emacs Lisp is of any help to any Emacs oldbies confronting this tech. The usual way to develop for it is with nFringe's pixel Mine and Visual Studio. And while Visual Studio is an excellent debugger, it lacks in the actual text editing department, as even Vi users know...

My tools of choice are Emacs AutoComplete mode, which gives Emacs intelli-sense like capabilities over a broad range of languages (it's pretty magical with Lisp, as you'd expect) and the good old Exuberant Ctags, primed to tag Unrealscript, with the following regular expressions in ~/ectags.cnf. I have to say ectags-select is very handy for working with these, as an alternative to the usual way of working with tags in Emacs as it lets you browse the enormous range of options that pop up.

These are the expressions to use for Exubrerant Ctags when tagging Unrealscript.




This is the support code I have: a set of interactive functions for invoking the udk, building code, running the editor. It creates a group of settings that can be customised with M-x customize-group udk. I watch the log in an emacs buffer via global-revert-tail mode. I have not yet added support for UDK log files to compilation mode, but thats an obvious tweak I'll try at some point. I've had enough fiddling with regular expressions for now.



PS: I thought I'd reproduce the unrealscript mode here, just in case. It lives where you put your other extra mode files.

Thursday, April 7

Bricolage - Now with font rendering.

The next step for the bricolage engine I'm writing is to add font rendering. Something one should add early on in development, so you can read all those panic messages from your machine and keep an eye on CPU usage and memory usage as you go along. For this I have been using Angel Code's BMFont tool, which is noticeably better than any other I've seen: even supports signed distance fields..




Monday, March 14

Bricolage engine starts rendering


My experimental bricolage lash-up has started rendering (actually, two weeks ago) .. here's a screenshot of it rendering the videogame testcard..next step is to get a nice font rendering module and debugging console going..before delving into 3d..


Friday, February 4

Bricolage progress.

The initial version of the Bricolage engine is complete. It uses nedmalloc for memory allocation, EASTL for generic containers and GLEW and GLFW for OpenGL and platform abstraction layers, although at the moment the only platform I'm supporting is Windows. It should be more than enough to get an image on the screen, run a shader, and build some basic component - entity architecture. So, here we go...

Tuesday, January 25

A New Engine

I am between projects at the moment, in a phase of experimentation. One thing I've wanted to do is construct a good testbed engine for experiments. However an entire engine is a major undertaking - three months to get the basics down pat. A third party engine always takes time to learn, though. There's nothing to replace the familiarity of something you coded yourself.

It occured to me that there is a third alternative. Bricolage - to assemble an engine or as much as possible of an engine from the multitude of FOSS libraries that are available. Such would be in accord with the UNIX philosophy: to have small programs or tools that do one thing well, and arrange it so that the sum of the parts are greater than the whole. This should give me a platform for experimentation in a much faster time than full self-assembly, but have the advantage of familiarity that comes with self-designed API's.

With no further ado, I introduce the Bricolage engine. I will blog about each component as I add it. Eventually it should be a reasonably featured engine that works with mingw on Windows and is ready for porting to other platforms. To quote Wikipedia:


Bricolage is a term used in several disciplines, among them the visual arts
and literature, to refer to the construction or creation of a work from a
diverse range of things that happen to be available, or a work created by
such a process. The term is borrowed from the French word bricolage, from
the verb bricoler, the core meaning in French being, "fiddle, tinker" and,
by extension, "to make creative and resourceful use of whatever materials
are at hand (regardless of their original purpose)"

My main goal is portability. I need to be able to support mutiple renderers and platforms if at all possible. This pretty much limits me to gcc as a compiler and OpenGL (and it's junior cousin, OpenGLES) as a rendering API. I chose SCons as a build tool, mainly for reasons of tast. It's as portable as Python is and I know it and Python reasonably well. CMake or autotools are equally valid choices.