Unreal Tournament 3 marks the return of the world's première first-person shooter. It unleashes the full power of Unreal Engine 3, taking graphics, gameplay, and challenge to a whole new level. Players engage in intense battles with other human players online, or against Unreal artificial intelligence that sets the industry standard. With the most powerful futuristic weapons and vehicles available, this is FPS action at its best!

Report article RSS Feed Unreal Learning #4: Advanced Weapons (part 2) - Material Switching

This tutorial is part of a series of three will show you how to create complex materials, and how to program a weapon which can alter them, and a physics gun.

Posted by ambershee on Feb 28th, 2008
Basic Server Side Coding.

This is the second part of the advanced weapons tutorial. You should have a custom mesh, and a custom material all set up ready for some coding - if you don't, you may want to step back and read the first part of the tutorial before you get started. Once again, let's start with the power cube.

The Power Cube

Let's create a class called PowerCube. We're going to want it to be a static mesh with physics properties. We're in luck, there's already a class which has just that for us - kActor. Even better, there's a class derived from that called kActorSpawnable, which we can create while the game is running if we want to - perfect.

uscript code:
class PowerCube extends KActorSpawnable;

DefaultProperties
{
}

Best of all, we don't need to write any real code - everything we want is in the default properties, and we just need to do some tweaking. We set bWakeOnLevelStart to true, meaning that when the game begins, the object will start to act under world physics. We don't want magically floating boxes that unpredictably fall back down to earth. We also add a static mesh component. I set the mesh to 'TutorialStuff.PowerCube' for the time being. And that's it!

uscript code:
class PowerCube extends KActorSpawnable;

DefaultProperties
{
Begin Object Name=StaticMeshComponent0
StaticMesh=StaticMesh'TutorialStuff.PowerCube'
Scale3D=(X=1.0,Y=1.0,Z=1.0)
WireframeColor=(R=0,G=255,B=128,A=255)
BlockRigidBody=true
RBChannel=RBCC_GameplayPhysics
RBCollideWithChannels= (Default=TRUE, GameplayPhysics=TRUE, EffectPhysics=TRUE)
End Object

bWakeOnLevelStart = true
}

The Charge Rifle

This is where things get a little tricker. We know this is going to be difficult, so how about we think about what we need before we start?

We want a weapon, so extending the Instagib Rifle could be a good start as it already has mesh and doesn't have a weapon group, which saves us a lot of hassle. The weapon needs to also contain material and colour information, so that we can change the visible appearance of the power cube when we want to. Well, that gives us some direction! I hope you remembered your colour choices and materials from the last part of the tutorial, because they're mighty handy right now.

uscript code:
class ChargeRifle extends UTWeap_InstagibRifle;

enum Colour
{
eRed,
eBlue,
eNeutral
};

var Colour Charge;
var bool Hard;
var linearcolor RedCube, BlueCube, NeutralCube;
var texture2d WeakMat, StrongMat;
var MaterialInstanceConstant NewMaterial;

DefaultProperties
{
RedCube = (R = 100, G = 5, B = 5, A = 1)
BlueCube = (R = 5, G = 5, B = 100, A = 1)
NeutralCube = (R = 100, G = 100, B = 5, A = 1)
WeakMat = texture2d'Envy_Effects.Energy.Materials.T_EFX_Energy_Swirl_03'
StrongMat = texture2d'Envy_Effects.Energy.Materials.T_EFX_Energy_Tile_01'
Hard = false
}

Next up, we need some logic. We need to be able to both colourise and apply new materials to our power cube. Let's write a function to do each.

We'll start with recolouring, as this should be nice and easy. I decided that our firing function should decide what colour we want the cube to be, so this function takes that colour, as well as the actor that has been shot by the weapon. We quickly check that our actor is indeed a power cube, and if it is, we create and set a new material instance from the material it already had, and find the PowerColour parameter - then we change it with our chosen colour.

uscript code:
function ReColour(LinearColor NewColour, Actor HitActor)
{
if (HitActor.IsA('PowerCube'))
{
NewMaterial = PowerCube( HitActor ).StaticMeshComponent.CreateAndSetMaterialInstanceConstant( 0 );
NewMaterial.SetVectorParameterValue('PowerColour', NewColour);
}

}

Materials wise, we only have two options and we're using the boolean 'Hard' to decide which one we need, so we'll give that function just the actor that the weapon hit, and decide the rest. We work out whether the weapon is currently set to hard or not, then create and set a new material instance based on that again, this time looking for the BaseTexture and RippleEffect parameters from before, and applying different textures to them. Very snazzy.After we've done that, we then make sure that the materials are the right colour, so we call recolour with the correct colour.

uscript code:
function ReMaterial(Actor HitActor)
{
if (Hard == true)
{
NewMaterial = PowerCube( HitActor ).StaticMeshComponent.CreateAndSetMaterialInstanceConstant( 0 );
NewMaterial.SetTextureParameterValue('BaseTexture', WeakMat);
NewMaterial.SetTextureParameterValue('RippleEffect', StrongMat);

Hard = false;
}
else
{
NewMaterial = PowerCube( HitActor ).StaticMeshComponent.CreateAndSetMaterialInstanceConstant( 0 );
NewMaterial.SetTextureParameterValue('BaseTexture', StrongMat);
NewMaterial.SetTextureParameterValue('RippleEffect', WeakMat);

Hard = true;
}

switch(Charge)
{
case eRed:
ReColour(RedCube, HitActor);
break;
case eBlue:
ReColour(BlueCube, HitActor);
break;
case eNeutral:
ReColour(NeutralCube, HitActor);
break;
}
}

Only one thing left! Our weapon use what colour we're firing, and what material we're firing - but it can't actually fire properly! We need a StartFire() function.

In this case, we just lifted and modified the CalcWeaponFire() function in the Weapon class. It doesn't deal any damage, or have any fancy effects, but it does send out a trace, and determine what we hit, which is enough for us. I decided that our first fire mode would cycle through the available colours, and our second would toggle the different materials.

To summarise what is commented in the code, we send out a trace and determine what we have hit. If we have hit a power cube, and our fire mode is the first, we change the colour, otherwise if the fire mode is not the first, we change the material. Additionally, we nicked and left in some code from CalcWeaponFire() that handles subsequent hits and portals. We'll leave that in for now because you never know, it might be useful later.

uscript code:
simulated function StartFire(byte FireModeNum)
{
local vector HitLocation, HitNormal, Dir;
local Actor HitActor;
local TraceHitInfo HitInfo;
local ImpactInfo CurrentImpact;
local PortalTeleporter Portal;
local float HitDist;
local vector StartTrace, EndTrace;
local array ImpactList;

StartTrace = InstantFireStartTrace();
EndTrace = InstantFireEndTrace(StartTrace);

// Perform trace to retrieve hit info

HitActor = GetTraceOwner( ).Trace(HitLocation, HitNormal, EndTrace, StartTrace, TRUE, vect(0,0,0), HitInfo, TRACEFLAG_Bullet);

// If we didn't hit anything, then set the HitLocation as being the EndTrace location
if( HitActor == None )
{
HitLocation = EndTrace;
}

// Convert Trace Information to ImpactInfo type.
CurrentImpact.HitActor = HitActor;
CurrentImpact.HitLocation = HitLocation;
CurrentImpact.HitNormal = HitNormal;
CurrentImpact.RayDir = Normal(EndTrace - StartTrace);
CurrentImpact.HitInfo = HitInfo;


// Add this hit to the ImpactList
ImpactList[ ImpactList.Length ] = CurrentImpact;

// check to see if we've hit a trigger.
// In this case, we want to add this actor to the list so we can give it damage, and then continue tracing through.
if( HitActor != None )
{
if (!HitActor.bBlockActors && PassThroughDamage( HitActor ))
{
// disable collision temporarily for the trigger so that we can catch anything inside the trigger
HitActor.bProjTarget = false;
// recurse another trace
CurrentImpact = CalcWeaponFire(HitLocation, EndTrace, ImpactList);
// and reenable collision for the trigger
HitActor.bProjTarget = true;
}
else
{
// if we hit a PortalTeleporter, recurse through
Portal = PortalTeleporter(HitActor);
if( Portal != None && Portal.SisterPortal != None )
{
Dir = EndTrace - StartTrace;
HitDist = VSize(HitLocation - StartTrace);
// calculate new start and end points on the other side of the portal
StartTrace = Portal.TransformHitLocation( HitLocation );
EndTrace = StartTrace + Portal.TransformVector( Normal( Dir ) * ( VSize( Dir ) - HitDist) );
//@note: intentionally ignoring return value so our hit of the portal is used for effects
//@todo: need to figure out how to replicate that there should be effects on the other side as well
CalcWeaponFire(StartTrace, EndTrace, ImpactList);
}
}

if (HitActor.IsA('PowerCube'))
{
if (FireModeNum == 0)
{
switch(Charge)
{
case eRed:
ReColour(BlueCube, HitActor);
Charge = eBlue;
break;
case eBlue:
ReColour(NeutralCube, HitActor);
Charge = eNeutral;
break;
case eNeutral:
ReColour(RedCube, HitActor);
Charge = eRed;
break;
}
}
else
{
ReMaterial( CurrentImpact.HitActor );
}
}
}
}

You might want to give that a good read through, and make sure you understand exactly what is going on. It's fortunately not too complicated at the end of the day.

So we're finished! Well, not quite. We have power cubes, and we have a very basic weapon that will change them. What we don't have are cubes and weapons in the game. I wrote a quick summon function that I can call from the console to drop one straight into the game for testing. I decided to use the Weapon Replacement mutator that is already in the game to test, but you could easily write your own mutator to replace the default inventory - just like we did in Unreal Learning #1. You can access the console in the game, by default pressing the tab button. Typing in the name of the function will allow it to happen - this is what the exec function is for. Note that because the function is in the weapon code, it will only happen when the weapon is drawn!

uscript code:
exec function SpawnCube()
{
local class CubeClass< SEMI >

CubeClass = class(DynamicLoadObject("Tutorial4.PowerCube", class'Class'));

Spawn( CubeClass,,, );
}

We're definitely now done! Compile the code, and give it a try. You should be able to spawn a power cube using the console command (doesn't that look odd but funky in game?), and use the super shock rifle (ChargeRifle) primary fire to change it's colour, and secondary colour to change it's material.

In the final part of this tutorial, we will look at how the power cube physics can be altered - and we'll add a physics gun to let us throw it around.

Until next time, happy modding!

Post comment Comments
Varsity
Varsity Feb 29 2008, 9:44am says:

That font size makes my eyes hurt. :-(

+1 vote     reply to comment
Mangopork
Mangopork Jul 29 2008, 12:56am replied:

Ctrl + Mousewheel.

Voila.

+1 vote     reply to comment
ambershee Author
ambershee Feb 29 2008, 10:23am says:

It was larger (it was formatted the same way as all the others). It must have been resized when authed 0_o

+2 votes   reply to comment
ambershee Author
ambershee Feb 29 2008, 10:30am replied:

In fact no, it shrink purely at random when saved. God this text editor is buggy...

+2 votes   reply to comment
p3gamer
p3gamer Mar 30 2008, 6:44am says:

I am trying to compile. Getting error: "ChargeRifle.uc<119> Error, Missing '<' in 'array' "
1. I am copying and pasting the code. 2. There is no code on line 119.
3. I have incrementally commented out lines of code and am still getting this error. Any ideas? Can you upload the source files?

Thanks,
new member

+1 vote     reply to comment
ambershee Author
ambershee Apr 7 2008, 8:01am replied:

Sorry I didn't get back to you sooner.

The text editor here has a merry habit of eliminating < > tags and treating it like a html tag, regardless of what you do.

Take a look at the StartFire function - I've spotted the issue right away. It should have an array of impactinfos;

local array<ImpactInfo> ImpactList;

+1 vote   reply to comment
p3gamer
p3gamer Apr 23 2008, 10:39am says:

Thank you, but I am getting a new error:

"Error, Type mismatch in Call to 'Spawn', parameter 1"

I found that Spawn() can take 5 parameters and that the first parameter is of type class. Here is my code for the SpawnCube function:

local class CubeClass;

CubeClass = class (DynamicLoadObject ("PowerCube.PowerCube", class 'Class'));

Spawn( CubeClass,,,, );

Am I missing something with my class declaration or something else?

+1 vote     reply to comment
ambershee Author
ambershee Apr 24 2008, 12:42pm says:

Looks like the text editor strikes back again. It has a habit of killing lines of code that use the angled brackets.

The lines should be:

exec function SpawnCube()
{
local class<PowerCube> CubeClass;

CubeClass = class<PowerCube>(DynamicLoadObject("Tutorial4.PowerCube", class'Class'));

Spawn( CubeClass,,, );
}

+1 vote   reply to comment
p3gamer
p3gamer Apr 24 2008, 5:00pm says:

Thanks again ambershee.

I, unfortunately have another issue.

"Begin Object Name = StaticMeshComponent()"

I must specify valid name for subobject/component.

Is StaticMeshComponent a data type and not a function, similiar to this:
"Begin Object Class=StaticMeshComponent Name=......."


+1 vote     reply to comment
ambershee Author
ambershee Apr 30 2008, 9:04am says:

I'll look into it - I'm a little busy.

I'll also be updating and moving a lot of tutorials soon, so we'll have a proper help forum, and a have all those editor issues fixed, thanks to the changes Intense made :)

+1 vote   reply to comment
qweasdzxc
qweasdzxc May 7 2008, 9:29pm says:

I am also trying to compile. But the error is: "ChargeRifle.uc<159> Error, Type mismatch in'=' "
the function is : simulated function StartFire(byte FireModeNum)

156:{
157:ReMaterial( CurrentImpact.HitActor );
158:}
159:}
160:}
161:}

i don't know where the '=' is in

+1 vote     reply to comment
Handshakes
Handshakes May 29 2008, 2:23pm replied:

I think the UT compiler takes out any empty spaces, so <159> in your text editor may not be <159> to the compiler.

+1 vote     reply to comment
Handshakes
Handshakes May 29 2008, 2:31pm says:

By the by, thanks for all the tutorials Ambershee. A+ stuff.

+1 vote     reply to comment
purplerainbo
purplerainbo Jul 7 2008, 8:37pm says:

Ok i think I fixed it, i compiled it and got no errors. I did get 2 warnings in PowerCube but i will worry about those later. The fix is

CubeClass == class(DynamicLoadObject("Tutorial4.PowerCube", class'Class'));

+1 vote     reply to comment
Tikithehut
Tikithehut Jul 31 2008, 5:04pm says:

Hello!

Thanks much for the tutorial, I feel like I'm getting a better grasp on the script. But I am coming up with some issues on this one. Overall it's just not working, I've tried it in published and unpublished.

The script compiles perfectly accept for the two warnings purplerainbo was talking about. But when I go into the game and try the console command "SpawnCube" nothing happens.

Also, when I go to use the weapon replacement mod to set the charge rifle, it's not one of the options available.

I'm also noticing the compiling process doesn't write a ".ini" file for the powercube. I don't know if this is because it's not actually supposed to be a mutator (that was my assumption). But to be on the safe side I went and wrote my own, including a bit in there about recognizing the charge rifle. This didn't work either, although it was funny to watch all the bots run around with instagib rifle models that wouldn't fire.

I'm pretty certain I followed the tutorial to the letter, and am comfortable in both the material editor and generic browser. Was wondering if anyone knew possible directions I might look to go about fixing this. Thanks for taking the time to write these!

p.s. I noticed the end of the charge rifle code has the ("Tutorial4.PowerCube", class'Class'));
Is this supposed to be the same package the cube is located within the Unreal Editor? If so should it be "TutorialStuff" like in part one of this tutorial? I could just be confused

+1 vote     reply to comment
purplerainbo
purplerainbo Aug 7 2008, 8:28pm says:

yea i still have not worked it out. I gave-up after trying to fix it, im giving it another go. I ended up removing the "Tutorial4.PowerCube" and replaced it with my own path. Ill let you know if i learn more.

+1 vote     reply to comment
Tikithehut
Tikithehut Aug 11 2008, 6:09pm says:

So I figured something out! At least in regards to the warnings.

This is specifically for anyone coming across the "unresolved reference" or similar error. This is caused by the package (where your static mesh for the power cube is located) being in a place the compiler/editor does not like. By default these packages seem to like to be created in the unpublished directory. However when compiling, the package needs to be copied over to the UTGame\Published\CookedPC directory.

Once I copied my packages over these warnings vanished when compiling. Good luck all!

+1 vote     reply to comment
Modham
Modham Jun 22 2009, 3:04am says:

Ambershee kind of left everyone hanging.. I guess we can thank him for taking us half way there on this one.

+1 vote     reply to comment
Y2Rimmer
Y2Rimmer Jan 9 2010, 4:34pm says:

Well I've finally got this tutorial working. To fix the issue with the cube not spawning, I had to edit the line:

CubeClass = class<PowerCube>(DynamicLoadObject("PowerCube.PowerCube", class'Class'));

Notice I'm using "PowerCube.Powercube" instead of "Tutorial4.PowerCube". PowerCube is the name of my mod (change this to your script's folder name). The spawn command should work fine now, assuming you've managed to equip the Change Rifle. I made an ini file for this, so it can be changed via the Weapon Replacement mod.

Now the cube has spawned you'll notice another problem. It doesn't have the texture you made. You can edit the mesh in Unreal Editor so it uses it. If anyone's still interested I don't mind giving further details.

+1 vote     reply to comment
Post a Comment
click to sign in

You are not logged in, your comment will be anonymous unless you join the community today (totally free - or sign in with your social account on the right) which we encourage all contributors to do.

2000 characters limit; HTML formatting and smileys are not supported - text only

Icon
Unreal Tournament 3
Platforms
Windows, PS3, X360
Developer
Epic Games
Publisher
Midway Games
Contact
Send Message
Official Page
Unrealtournament3.com
Release Date
Released Nov 18, 2007
Game Watch
Track this game
Tutorial
Browse
Tutorials
Report Abuse
Report article
Related Games
Unreal Tournament 3
Unreal Tournament 3 Single & Multiplayer First Person Shooter
Related Engines
Unreal Engine 3
Unreal Engine 3 Commercial Released Aug 31, 2007
Related Groups
Epic Games
Epic Games Developer & Publisher
Shee Labs Game Development
Shee Labs Game Development Developer & Publisher with 14 members
Unreal Tournament 3 Mod Developers
Unreal Tournament 3 Mod Developers Fans & Clans group with 232 members