Metronome

So, I decided to try and build a Kismet-based metronome system so that I can trigger small musical elements (reliably through a sort of music sequencer type system) and also in-game events (eg. enemy spawn, explosions, etc…) in time with the music. Originally I tried to do this directly in Kismet using looping Delay objects, but I found that they didn’t keep reliable time.

The first attempt involved using the SetTimer() function, which was set to the 16th beat division of a given tempo (eg. 125ms @ 120bpm). This was then used to ‘fire’ a custom Kismet event. But again, I found that the timing was unreliable.
After some research, I surmised that this was due to the fact that Actors in UDK are updated every tick and so the delay function could be out by a full frame length (approx. 20ms on my system).

So, I decided to do the following:
Establish the ‘target’ time of the next beat division by getting the system time and adding the beat division length (eg. 125ms) to this.
Then use the Tick() function to check whether the elapsed time was equal to the ‘target’ time, using the DeltaTime variable (returned within the Tick function).
Now, the elapsed time is unlikely to actual equal the target time so I built in a ‘checking system’ to see if the elapsed time was within 1/2 of a frame length (either under or over) and if so this would be used as the 16th beat trigger. This is not ideal, but I figured that it would only be around 10ms out either way which should be close enough (for now anyways…)
I was also counting the number of 16th beats so that I could trigger 8th/4th/etc. beats as well.

The problem is that when I set up a basic Kismet system to trigger off a sound using my ‘trigger’s it wasn’t keeping reliable time when compared to a long looping drum beat at the same tempo — it feels like its out by more than 10ms.

___________________________________________________________________________

Here’s the code for the Timer (this currently a placeable actor rather than a Kismet Action as at the time this seemed easier, but it will become Kismet-based eventually):

/*
// actor to act as a metronome
// uses time elapsed to check against target time intervals - performed every tick
// setting the ShouldBeOn property (via Kismet) does the following
// - 1 = activate
// - 2 = deactivate
// tempo calculations performed on activation
//
// CHANGES:
// would using an average of DT help with accuracy (would possibly counter effects of occasional long tick time)????
// need to fire off all Kismet events & update 16thBeat Count on start
//
*/
class AwesomeTimerActor extends Actor
 hidecategories(Attachment, Physics, Debug, Object) // hide these categories in the properties of this actor
 placeable;
var StaticMeshComponent MyMesh;
var int ShouldBeOn;
var float Tempo;
var float DivLength16th;
var int Year, Month, DayOfWeek, Day, Hour, Min, Sec, MSec;
var int SystemStartTimeIntA, SystemStartTimeIntB;
var int DeltaTimeMsec, accDT, TargetTime, CurrentTime, Count16thBeats;
var int StartTime;
function Tick(float DeltaTime)
{
 //`log(DeltaTime);
if(ShouldBeOn == 1) //start Actor functionality
 {
 //`log("======================================== ShouldBeOn == 1");
GetSystemTime( Year, Month, DayOfWeek, Day, Hour, Min, Sec, MSec ); //Get the system time
 //`log(" Year:" $ Year $ " Month:" $ Month $ " DayOfWeek:" $ DayOfWeek $ " Day:" $ Day $ " Hour:" $ Hour $ " Min:" $ Min $ " Sec:" $ Sec $ " MSec:" $ MSec); //log system time
SystemStartTimeIntA = (Year * 10000) + (Month * 100) + (Day); //combine Year, Month & Day - done to preserve leading 0's
 //`log("SystemStartTimeIntA: " $ SystemStartTimeIntA);
 SystemStartTimeIntB = (Hour * 10000000) + (Min * 100000) + (Sec * 1000) + (MSec); //combine Hour, Min, Sec & MSec - done to preserve leading 0's
 //`log("SystemStartTimeIntB: " $ SystemStartTimeIntB);
DivLength16th = ((1000 / (Tempo / 60)) / 4); //calculate lenth (in msec) of 16th beat division
 //`log("DivLength16th: " $ DivLength16th);
Count16thBeats = 1; //number of 16th beat divisions
ShouldBeOn = 3; //set to 3 so can use another if check - used to move into the metronome system
 }
if(ShouldBeOn == 2) //stop Actor functionality
 {
 //`log("========================================= ShouldBeOn == 2");
 ShouldBeOn = 0;
 }
if(ShouldBeOn == 3) //metronome system
 {
 DeltaTimeMSec = DeltaTime * 1000; //convert DT from secs to msecs
 //`log("DeltatimeMSec: " $ DeltaTimeMSec);
 accDT += DeltaTimeMSec; //how much time has elapsed (in msec) since start
 //`log("accDT: " $ accDT);
 TargetTime = SystemStartTimeIntB + (DivLength16th * Count16thBeats); //calculate target time of next 16th beat division
 //`log("TargetTime: " $ TargetTime);
 CurrentTime = SystemStartTimeIntB + accDT; //increase current time
 //`log("CurrentTime: " $ CurrentTime);
 
 if(CurrentTime == TargetTime) //if current time is equal to target time (unlikely, but possible)
 {
 //`log("16th Beat Trigger - equal");
 TriggerKismet16thBeat(); //Trigger Kismet 16th Beat
if(Count16thBeats % 2 == 0) //check for 8th Beat
 {
 //`log("8th Beat Trigger");
 TriggerKismet8thBeat(); //Trigger Kismet 8th Beat
 }
if(Count16thBeats % 4 == 0) //check for 4th Beat
 {
 //`log("4th Beat Trigger");
 TriggerKismet4thBeat(); //Trigger Kismet 4th Beat
 }
if(Count16thBeats % 16 == 0) //check for 1 Bar
 {
 //`log("1 Bar Trigger");
 TriggerKismet1Bar(); //Trigger Kismet 1 Bar
 }
if(Count16thBeats % 32 == 0) //check for 2 Bar
 {
 //`log("2 Bar Trigger");
 TriggerKismet2Bar(); //Trigger Kismet 2 Bar
 }
Count16thBeats++; //increase count of 16th beat divisions
 }
else if(CurrentTime >= (TargetTime - (DeltaTimeMSec / 2)) && CurrentTime <= TargetTime) //if current time is less than 1/2DT under target time (ie. close enough)
 {
 //`log("16th Beat Trigger - under");
 TriggerKismet16thBeat(); //Trigger Kismet 16th Beat
if(Count16thBeats % 2 == 0) //check for 8th Beat
 {
 //`log("8th Beat Trigger");
 TriggerKismet8thBeat(); //Trigger Kismet 8th Beat
 }
if(Count16thBeats % 4 == 0) //check for 4th Beat
 {
 //`log("4th Beat Trigger");
 TriggerKismet4thBeat(); //Trigger Kismet 4th Beat
 }
if(Count16thBeats % 16 == 0) //check for 1 Bar
 {
 //`log("1 Bar Trigger");
 TriggerKismet1Bar(); //Trigger Kismet 1 Bar
 }
if(Count16thBeats % 32 == 0) //check for 2 Bar
 {
 //`log("2 Bar Trigger");
 TriggerKismet2Bar(); //Trigger Kismet 2 Bar
 }
Count16thBeats++; //increase count of 16th beat divisions
 }
else if(CurrentTime <= (TargetTime + (DeltaTimeMSec / 2)) && CurrentTime >= TargetTime) //if current time is less then 1/2DT over target time (ie. close enough)
 {
 //`log("16th Beat Trigger - over");
 TriggerKismet16thBeat(); //Trigger Kismet 16th Beat
if(Count16thBeats % 2 == 0) //check for 8th Beat
 {
 //`log("8th Beat Trigger");
 TriggerKismet8thBeat(); //Trigger Kismet 8th Beat
 }
if(Count16thBeats % 4 == 0) //check for 4th Beat
 {
 //`log("4th Beat Trigger");
 TriggerKismet4thBeat(); //Trigger Kismet 4th Beat
 }
if(Count16thBeats % 16 == 0) //check for 1 Bar
 {
 //`log("1 Bar Trigger");
 TriggerKismet1Bar(); //Trigger Kismet 1 Bar
 }
if(Count16thBeats % 32 == 0) //check for 2 Bar
 {
 //`log("2 Bar Trigger");
 TriggerKismet2Bar(); //Trigger Kismet 2 Bar
 }
Count16thBeats++; //increase count of 16th beat divisions
 }
 //`log(""); //used to put a line break into log to separate data streams
 }
}

function TriggerKismet2Bar()
{
 //`log("============================= Timer2Bar");
 TriggerEventClass(class'AwesomeSeqEvent_Timer2Bar', self);
}
function TriggerKismet1Bar()
{
 //`log("============================= Timer1Bar");
 TriggerEventClass(class'AwesomeSeqEvent_Timer1Bar', self);
}
function TriggerKismet4thBeat()
{
 //`log("============================== Timer4thBeat");
 TriggerEventClass(class'AwesomeSeqEvent_Timer4thBeat', self);
}
function TriggerKismet8thBeat()
{
 //`log("================================== Timer8thBeat");
 TriggerEventClass(class'AwesomeSeqEvent_Timer8thBeat', self);
}
function TriggerKismet16thBeat()
{
 //`log("================================== Timer16thBeat");
 TriggerEventCLass(class'AwesomeSeqEvent_Timer16thBeat', self);
}
defaultproperties
{
 CustomTimeDilation=1.0
Tempo=120
ShouldBeOn=0
SupportedEvents.Add(class'AwesomeSeqEvent_Timer2Bar')
 SupportedEvents.Add(class'AwesomeSeqEvent_Timer1Bar')
 SupportedEvents.Add(class'AwesomeSeqEvent_Timer4thBeat')
 SupportedEvents.Add(class'AwesomeSeqEvent_Timer8thBeat')
 SupportedEvents.Add(class'AwesomeSeqEvent_Timer16thBEat')
// add a static mesh with a material
 Begin Object Name=PickupMesh
 StaticMesh=StaticMesh'UN_SimpleMeshes.TexPropCube_Dup'
 Materials(0)=Material'EditorMaterials.WidgetMaterial_Y'
 Scale3D=(X=0.125,Y=0.125,Z=0.125)
 End Object
 Components.Add(PickupMesh)
 MyMesh=PickupMesh
}

___________________________________________________________________________

Here’s the code for the Kismet Events:

class AwesomeSeqEvent_Timer16thBeat extends SequenceEvent;
event Activated()
{
 //`log("=============================== Timer8thBeat EventActivated");
}
defaultproperties
{
 MaxTriggerCount=0
 ObjName="Timer16thBeat"
 ObjCategory="Awesome Game"
 VariableLinks.Empty
 bPlayerOnly=false
}

______________

class AwesomeSeqEvent_Timer8thBeat extends SequenceEvent;
event Activated()
{
 //`log("=============================== Timer8thBeat EventActivated");
}
defaultproperties
{
 MaxTriggerCount=0
 ObjName="Timer8thBeat"
 ObjCategory="Awesome Game"
 VariableLinks.Empty
 bPlayerOnly=false
}

_______________

class AwesomeSeqEvent_Timer4thBeat extends SequenceEvent;
event Activated()
{
 //`log("=============================== Timer4thBeat EventActivated");
}
defaultproperties
{
 MaxTriggerCount=0
 ObjName="Timer4thBeat"
 ObjCategory="Awesome Game"
 VariableLinks.Empty
 bPlayerOnly=false
}

_______________

class AwesomeSeqEvent_Timer2Bar extends SequenceEvent;
event Activated()
{
 //`log("=============================== Timer2Bar EventActivated");
}
defaultproperties
{
 MaxTriggerCount=0
 ObjName="Timer2Bar"
 ObjCategory="Awesome Game"
 VariableLinks.Empty
 bPlayerOnly=false
}

_______________

class AwesomeSeqEvent_Timer1Bar extends SequenceEvent;
event Activated()
{
 //`log("=============================== Timer1Bar EventActivated");
}
defaultproperties
{
 MaxTriggerCount=0
 ObjName="Timer1Bar"
 ObjCategory="Awesome Game"
 VariableLinks.Empty
 bPlayerOnly=false
}

___________________________________________________________________________

Now, I’m not sure if the unreliable timing is just the way it is within UDK without proper access to the underlying engine code (I’m not a developer) or if its due to inefficiencies in my code..?

Advertisement

Lego Blocks 01

Today I started a new project – the vehicle jump is working and will do for now…

The new project is to create a little system that uses lego bricks to illustrate the difference between using pre-rendered tracks of audio vs using individual samples and the increase in variety that comes as a result. This will be used as part of a conference presentation for the upcoming AES Conference (http://www.aes.org/events/130/).

Essentially there will be a pile of lego bricks that form into various structures and can be shot to fall apart (or not depending on the metaphor).
Secnario:
– Pile of blocks
– Press E
– Turns into a castle
– Shoot castle
– Falls apart into pile of blocks
– Press E
– Turns into a boat (or other structure)
– Shoot boat
– Falls apart into pile of blocks
– Press E
– Turns into a ‘solid’ castle
– Shoot castle
– Castle falls over / wobbles but doesn’t break

In case you hadn’t figured it out, the structures that fall apart represent the use of samples as they can be re-arranged into different configurations, while the ‘solid’ castle represents the use of pre-rendered tracks as they are fixed in a single configuration.

Now, for what is only a brief illustration as part of a larger presentation, this project represents a significant amount of work as the actual system that controls the Actors is likely to be rather complex when using Kismet – it’s not really designed for this level of control. The ideal would be to create the system using scripts created for this specific implementation. However, as previously stated my intention is always to use as little “extension-ality” (if such a word exists…) – the main reason being here that I don’t really have the time to figure out how to do that at this point and I reckon I can get Kismet to ‘fake’ such a system even if it’s not the most elegant solution!

We got our excellent asset designer Chris (http://cjforde-gameart.blogspot.com/) to create some lego block Static Meshes which look good and I set to…

I created a test environment (just a simple room) and imported one of the blocks as a KActor (so that it would respond to Physics and so can be ‘shot’) and built a small structure.
Without any further setting of properties this worked exactly as desired – and looked pretty cool too!

Great. But then came the problem of how to get them to reform back into a castle structure from a pile…??

The first route was to try and create a Matinee sequence that started with the blocks in the desired structure position. I found this when I was creating something else and had set up a Matinee which had some Actors situated in a different location to where they were actually positioned – don’t ask, I thought I’d copied and pasted them. It was late!). Anyway, my idea was to use this to move the blocks from a pile (actual location) to defined structure (Matinee position).
To do this required all the blocks properties to be set so their Movement = PHYS_Interpolating.
Only one problem, this now meant that the blocks don’t respond to being or shot (or any other physics), as this requires the same property to be set to PHYS_RigidBody.

It was obvious that to get both behaviours (respond to physics and be ‘movable’) would mean changing the PHYS property – this can be done using the Set Physics action within Kismet.
For some reason at this point I made the decision to stop using the Matinee method and try and set the location of the Actors explicitly using x,y,z coordinates – the thinking being that I’m sure the Matinee uses a ‘relative movement’ approach to the location of Actors and this might cause problems when the prototype system is combined with the larger level being used for the AES presentation.
– I don’t have any real evidence for this notion, it seems to make sense given my experience of using Matinees and so I thought I’d try and preempt any potential problems…

I produced a system that started withe the blocks arranged in a structure, set to PHYS_RigidBody, and obtained their location and rotation using the Get Location and Rotation action (triggered on Level Start). The blocks respond to being shot and fall into a pile. When the Player presses E a Set Physics action is triggered to switch the blocks to PHYS_Interpolating, followed by a Set Actor Location which uses the original location and location vectors.
The image below shows a Kismet sequence that should help to illustrate what I mean (there’s one of these for each block)
This works quite nicely – when the original structure is shot it falls into a pile and then when the Player presses E the blocks jump to their original location (Ideally I wanted a nice animation to the movement, but that can wait!).
Because each the ‘block reset’ sequences are chained sequentially the blocks move one after the other (very quickly mind) and the effect is quite good – it’s almost like a stop-frame animation…
Occasionally there are little glitches where some of the blocks don’t return to their original positions and just hang in mid-air! It looks pretty cool actually, but will need to be resolved.

Next steps:
– Investigate whether Matinees can be used to ‘reset’ blocks instead of getting and setting Actor locations (will need to test for ‘relative movement’ and see if it’s a problem).
— Would produce easier to manage Kismet sequences as a single Matinee may be able to control multiple Actors(??)
— Can setup multiple Matinee sequences for each structure and just switch between them in the desired order
— May prevent glitches…
— Could maybe cheat and just create a Matinee sequence that animates the behaviour of the ‘solid’ castle (wobbling / falling)
— Would this need to be a different movement if the player moves round the structure before shooting?
—- if only for the presentation, may not be an issue – just don’t move!
— Can use the same method of switching PHYS mode between RigidBody and Interpolating as required
— Will the blocks stay in position when switched back to PHSY_RigidBody or will they fall?

Vehicle Jump 04

Today’s aim was to determine whether using vector coordinates to determine the vehicle’s progress down the slope would be more effective than the current system.

So the vector coordinates were broken down into x, y, z and checked to see which one described the vehicle’s motion down the slope towards the jump – turns out it was x (well it was going to be that or y).
The x value was evaluated against the x for the Note and scaled into a factor (0-1). This was plugged into the system in place of the previous method.

It appeared to work well at first but when tested with a straight run the vehicle wasn’t reaching the jump in time!

After some investigation (and much head scratching!) I’ve put it down to timing inconsistencies as a result of using a looping delay rather than a more ‘code-based’ approach – such as using CPU ticks. I’ve also moved over to the March release of UDK, but I don’t think that will have affected the timing (but you never know…!).
– the reason for avoiding the ‘code-based’ approach is that I want to achieve as much as is possible using vanilla objects (or at least easily adaptable versions, such as vehicles) so that it is accesible to as many people as possible.

I had to set the time value that represents the length of the music track to a value around 29.0 (the music is actually 32.2secs) in order to compensate for the timing issues.
– if this system were to be implemented into a game system it should be possible (with the help of a friendly programmer) to set up a much more accurate timing system — I might look into this at some point when I have the time…

I’ve also tweaked the MaxSpeed value used to increase the vehicle’s speed to improve consistency as a result of testing a variety of winding routes down the slope.

At the moment it appears to be working fairly well (although still some room for a few more tweaks, I think…).

Next steps:
– Extensive testing to check reliability – also get others to test to check I’m not ‘cheating.
– More tweaking of vehicle variables to increase stability – likely something to do with steering angles
– Make it look nice! (or at least better than it does now)
– Might be worth investigating what happens if the angle of the slope is not constant – would make for a more interesting run.

Vehicle Jump 03

So, I started off today by creating a custom [Modify Property] object in an attempt to be able to modify the MaxSpeed property of the vehicle – the idea being to be able to pass a variable into the object (something the vanilla [Modify Property] object can’t do).
There’s me thinking I’m all clever, but it turns out it doesn’t work as the vehicle (Actor) doesn’t have a “handler” for my “GAT_ModifyProperty” class – this would make sense when you think about it – all Actors recognise the “ModifyProperty” function thanks to the inheritance hierarchy, but obviously the Epic developers didn’t know I’d be creating my own version and so didn’t build that into the functionality of their classes!

Rather than rebuild the entire vehicle hierarchy to include this functionality – that would be quite a bit of work – I implemented a simpler Kismet system using [Modify Property] objects with defined values for the MaxSpeed property to see if it worked as I hoped.
The system evaluates the speed scaling factor (defined in a previous post):
– if sf > 1.0 then apply a MaxSpeed of 3500
– if sf == 1.0 then apply a MaxSpeed of 2750
– if sf < 1.0 then apply a MaxSpeed of 2500

The hope being that if the vehicle is falling behind the pace needed to ‘keep up’ with the music then the sf will produce a value > 1.0 , this will then cause the MaxSpeed to be increased which will allow the vehicle to accelerate (thanks to the adjusted TorqueVSpeedCurve parameter) in an attempt to catch back up with the music. Once it’s there the MaxSpeed will be reset back to 2750, or maybe even the lower speed of 2500 if it’s overshot the music’s progress.

Turns out this works quite nicely!
Because the vehicles progress is being evaluated fairly frequently (every 0.1secs) the changes in speed are not really obvious (although there is room for some minor tweaking…).

Turning the vehicle slows it down quite a bit so if the player does an excessive amount of veering around obstacles the vehicle can’t get up enough speed to keep pace with the music.
So, the ThrottleSpeed parameter was adjusted to enable the vehicle to accelerate back up to full speed quicker after a forced slow down – this also had the added side benefit of enabling the vehicle to accelerate beyond 2750 a little faster when the MaxSpeed is increased.

I’ve also added a [TriggerVolume] after the jump so that when the vehicle lands a musical stinger is player which works nicely.

The system is working well and is ensuring that in all but ‘extreme’ cases (see below) the vehicle always reaches the jump at the correct musical point.

Next steps:
– The vehicle is still a little twitchy when veering round obstacles at speed – need to investigate the effects of various parameters within the vehicle class.
– When the vehicle hits the jump it bottoms out which produces a fairly loud impact sound – look into tweaking the suspension settings and/or ride height.
– Occasionally when restricting the MaxSpeed to a lower value than the current speed the vehicle can jerk a little – have a look at creating a ‘stepped’ system that uses chained [Modify Property] objects to gradually decrease the speed.
– If the player hugs the side of the test environment (‘extreme’ case) the system does not quite produce the desired effect. The reason for this is that the system evaluates the distance between the vehicle and a [Note] placed in the centre of the jump. This means that even when the vehicle is on the ramp, the system determines it to be a reasonable distance and so increases the vehicle speed unnecessarily. The image below attempts to illustrate the problem (the X represents the [Note]).
The solution (hopefully) may be to use x,y,z vectors but only compare either the x or y (which ever describes the movement down the slope) – this should produce a consistent result no matter where the vehicle is across the slope.
– If the player releases the throttle (ie. they bottle it – the wimp!) then the vehicle very quickly comes to a stop and so therefore would require an excessive speed increase to catch back up with the music – look into setting up ‘auto-throttle’ when on the slope, and/or adjust the parameter(s) that determine how the vehicle slows down.

Vehicle Jump 02

Today I added numerous obstacles to the test environment – no imagination, they’re all the same: just a load of rocks!

As a result of the obstacles I discovered that by default the Scorpion vehicle is a real pig to steer when traveling at speed and so spent a lot of time crashing into rocks. Time to tweak the vehicle!
I found this page on the Epic Developers site very useful: http://udn.epicgames.com/Three/VehiclesTechnicalGuide.html
It contains definitions/descriptions of the key parameters and functions within the extensive hierarchy of vehicle classes – it also, rather handily, breaks down the inheritance aspects of the classes so you know where to find the functions you’re looking for.

A tip for when you’re tweaking a vehicle (or any other relatively complex class for that matter): where possible, do it one parameter at a time. Otherwise it gets very difficult as it can be hard to work out which parameter is having what effect on the system. It can also be tricky remembering the original state of multiple values for when you find that the 4 variables you’ve just changed have had a seriously negative effect on the system and you need to reset them!

I spent quite a long time tweaking various variables and have now got a vehicle which is relatively responsive when driving at speed (or at least in comparison to the original Scorpion), but also one that doesn’t flip over and/or start spinning in circles when you try to turn.
I found that these variables (all within the UTVehicle_Scorpion.uc class) proved the most useful in achieving this aim:
– MaxSteeringAngleCurve
– SteeringReductionFactor
– SteeringReductionMinSpeed
– SteeringReductionSpeed
– SpeedBasedTurnDamping

Once I had a drivable vehicle the next step was to figure out how to scale the speed of the vehicle to ensure it arrived at the end of the slope at the correct musical point. To do this I needed to know the current time position of the music and the current position of the vehicle as it travels down the slope. These values would then enable me to produce a scaling factor which could be applied to the MaxSpeed property of the vehicle to enable it to accelerate if the player deviates from the most direct route down the slope.

After a bit of head scratching I came up with the following system:
current time position / length of music (31.2secs) = amount of time left as a factor (ie. 0-1)
distance travelled down slope / length of slope (90700 units) = distance left to travel as a factor (ie. 0-1)

These 2 factors could then be ‘compared’ to produce a new factor which would be the scaling factor to be applied to the MaxSpeed property:
distance factor / time factor = speed scaling factor (ie. 0-1)

I then came across a bit of a stumbling block – namely that the [Modify Property] Action within Kismet doesn’t allow for the passing of values into it – I could have sworn that it did! Previously I’ve been using fixed values entered directly into the properties field of the object.
If this is correct, then I will have to come up with another cunning solution which may involve the scripting of a custom modify property object…
Unfortunately I did not have time today to investigate this further and so it will have to wait for another time.

Next steps:
– Investigate [Modify Property] and/or alternatives
– Experiment with different acceleration values to ensure the speed changes subtly

Things to think about:
– Modify health of vehicle so that if it crashes into an obstacle it explodes and so level needs to be restarted. This will save on creating complex switching mechanisms for resetting. Also a crash significantly impairs progress and so a significant speed boost would be required to ‘catch back up’ again. I’ve tried lowering the health to 30 (original =300), but then when the vehicle hits the jump it inflicts too much damage and it explodes. Investigate suspension settings / ride height to see if this can be prevented. The alternative may be to put DamageVolumes round all obstacles, but this would be a real pain!

Vehicle Jump 01

I’ve started a new little project which involves the manipulation of game states based on the musical soundtrack.

The idea is that you are driving a vehicle down a hill towards a jump over some obstacle, the sequence has been timed and a piece of music composed so that if the player takes the direct, straight route they will arrive at the jump at the correct point in the music (ie. a dramatic pause). However, there will be a number of obstacles en route which will cause the player to deviate from the most direct route – therefore arriving too late for the music to be in sync with the action.
The intention is to vary the speed of the vehicle to ensure that the player reaches the jump at the correct point, musically speaking.

The aim is that this should be done in such a way so that the player doesn’t realise that the mechanics of the game are being manipulated.

Currently I have a very basic prototype/testing environment which consists of a flat starting section to enable the vehicle to get up to speed, a transition to a sloped sequence, a  little ramp at the bottom of the slope and a final flat section to land on.

As the vehicle transitions onto the slope a [TrigggrVolume] starts the music which has been composed to last the time it takes to reach the bottom. I have created my own version of the Scorpion vehicle and adjusted the speed slightly to fine-tune the timing of the mechanics.
This process took quite a bit of trial and error to get things aligned correctly…

In anticipation of the need to to be able to increase the speed of the vehicle whilst it’s driving (without player input) I experimented with adjusting the various speed related properties using the [Modify Property] Action within Kismet
– MaxSpeed
– GroundSpeed
– AirSpeed
Whilst these would change I didn’t notice any appreciable change in the vehicle’s speed. So, I tried using the [Set Velocity] Action to see if that had any affect; I used the [Get Velocity] Action to determine what speed the vehicle was travelling at. This kind of worked, but was quite clunky – the vehicle would jerk when the velocity suddenly changed to new value – so that wasn’t going to work either!

After a bit of experimenting and reading of various posts on the forums it transpired that the “TorqueVSpeedCurve” within the vehicle’s script determines how the vehicle accelerates at different speeds. It turned out that the existing Scorpion vehicle (and therefore mine) was set so that when it reached a speed of 2000 it’s acceleration would be set to 0 – essentially meaning that no matter what the Speed properties were set to the vehicle would never exceed a speed 0f 2000.
So, I adjusted the Curve parameters so that the theoretical max speed was in excess of the current cap applied by the MaxSpeed/GroundSpeed/etc properties.
Now I can use the [Modify Property] Action to set the Max Speed to a higher value and the car will accelerate up to that new speed.

Next steps:
– Create obstacles within environment
– Determine calculations to analyse the distance from jump and to therefore scale max speed accordingly
– Fine tune the handling of the vehicle to make it more responsive to dodge obstacles, but also more stable – it has a habit of rolling over at speed at the moment
– Experiment with different acceleration values to ensure the speed changes subtly