Sunday, October 13

The Endless Quest for the Great Common Lisp Game

I have decided to revive my quest for the Great Common Lisp Game (abbreviated to TGCL).

 There are a number of reasons for this. Firstly, on the personal level the current day gig is heavyweight C++, and Common Lisp is a pleasant counterpoint to the tyranny of the Compile-Link-Edit cycle.

Secondly, the Lisp Commons has improved immensely over the last few years, thanks mainly Zach Beane and his efforts with Quicklisp.

Thirdly, the indie games scene has changed out of all recognition since the time I was trying to boot something; should I get to the point where I have something viable a pleotrha of funding options exist where there were none before. As a corollary with the third point, the nature of what I'm competing with has changed - I'm no longer competing against AAA developers with deep pockets, but people with a Unity3D liscence and more artistic talent than I can ever hope to have.

Most importantly, on the fifth hand ( we are counting on Lisp Alien hands ), I still have the deep suspicion that games development with Lisp looks deeply unlike anything with C++. I recally in the early days of my education as a Lisp neophyte that I was impressed with the GRT raytracer I was surprised to find that it was an abandoned project. I was even more surprised when I spoke to the author and he told me the reason for abandonment was that it "wasn't lispy enough.". This flipped a few relays on the cerebral switchboard: if a conventional raytracer architecture isn't "lispy enough", what of conventional game engine architecture? I'm not at all sure, but an extended bout of exploratory programming might be in order. So, onwards...

Wednesday, January 2

More fun with EProject

I am really enjoying using Jonathan Rockway's Eproject for Emacs. It's project management done in a minimalistic way that lends itself to many useful elisp hacks. As well as the scons/flymake project definition that I use, there's also a nifty hack that allows header/source file definition switching that I just cooked up.

It's only a few lines long but already useful. It's simple because eproject will simply list all the files in the current project for you, then you can pick out the duplicate...

Sunday, November 4

Scaling Emacs

I just finised a short stint working on UE4. One of my problems was Emacs. It just isn't scaling for the kind and size of projects that I have to tackle. UE4 is much more decomposed than UE3 and there are dozens and dozens of directories to tag. While Exuberant Ctags is an excellent technology for tagging source, it's slow to run over many, many directories.I need an intelligent daemon that can watch directories for change and re-run ctags as needed.

The second problemis that analysing source isn't the only route to finding semantic information these days: both the JVM and .NET support reflection - and this is the preferred route to find things like type names, function signatures, etc. Some code has to be written that can reflect on assemblies and class files and be fed into a tags database or auto-completion solution so that the class libraries can be discovered interactively.

I'm hoping there are open source projects there that would get me at least halfway to this functionality.

The third problem, which I'm likely to tackle myself is flymake. Flymake is predicated around the assumption that full builds are too expensive to do, and selective builds of a single file, in order to check it are the way to go. This does not fit with many build tools which manage large amounts of dependencies on a big project and build only the one changed file in any case. When flymake originated this was far too an expensive an operation to be performed interactively, but in a world of multi-gigabyte memory, and sixty-four-bit multicore this is no longer true. So I'm going to change that aspect of it.

Any more suggestions welcome.

Sunday, September 16

Building SDL_image for Android

SDL 2.0 for Android is coming along nicely - although it still has no support for NativeActivit There are good build instructions for it in the repository.

Adding SDL_image to the mix wasn't as simple as just copying it to the jni/ directory and calling "ndk-build". Doing so yielded this error:
jni/SDL_image/IMG_jpg.c:34:21: fatal error: jpeglib.h: No such file or directory

Logically enough, SDL_image needs libpng, libz, and libjpeg to build.Fortunately - as a time saver - you can still grab copies of these on on the libSDL website. You can then copy jpeg/ and png/ into the jni/ folder and: "ndk-build clean", followed by "ndk-build" to rebuild.

EDIT: It turns out there's a more recent framework here. All you will have to do is update the Android.mk in jni/src with the libraries sources you use, and be sure to load the right libraries in SDLActivity.java, of course.

Monday, June 25

Exporting FBX from Blender to UDK

While playing about with Blender 2.63a I discovered that it is now possible to export from Blender to the UDK via FBX. At present I'm not sure of the limitations: I set up a test animation with Rotation keys only, the armature origin at 0,0,0 and the mesh origin at 0,0,0, with no scaling. I ran the exported FBX through the FBX Converter for FBX 2012.2  to convert it into a binary: (as Blender's FBX exporter seems to export an antique version of FBX that the UDK takes exception to). To my surprise the converted FBX imported into the UDK fine - last year this was a sure fire recipe for disaster; something must have changed.

This screenshot shows the animation part of the process, adding keys one by one in pose mode. Note I use the LocRot keyset; it's very unlikely that the toolchain will support scale keys; so I haven't tried this. Armature and Mesh are centered on the origin (for the mesh this can be achieved by Object|Apply|Location and Object|Apply|Scale.

Add caption

Tuesday, April 24

SCons and Flymake.

Getting Emacs's flymake-mode to work with SCons was surprisingly easy. It needs some extra elisp in your .emacs to invoke flymake, which can be lightly adapted from the existing makefile processing, and a small check in the SConstruct to see if the process has been invoked by flymake itself. In my case, I arrange for a command line flag SYNTAX=1 to be passed from Flymake to SCons. For example..

The Emacs side involves some lightly edited versions of flymake functions in the .emacs as the scons command line is as make-like as it's sanely possible to be; a design goal of the SCons guys

The main drawback of this method is that it does not check header files directly, as SCons does not process them as they produce no direct build products. This leads me to suspect that SCons is not really the correct tool for this job; although a env.SyntaxCheck command could probably be rigged up: another post for another day.

EDIT: After some more work on this, I got header files working. The snag is that I've had to customise flymake-master-file-dirs to be relative to the directory in which include files are found include in my case. Which is probably not ideal - they should be relative to the buildfile.

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 :-)