The next part of the popular multiplayer shooter series is coming - and, according to the developers, Unreal Tournament 2007 will bring a lot of fresh ideas and solutions. The game will use the breathtaking Unreal 3 Engine. It is confirmed that Bombing Run, Assault and Domination gameplay types will be no longer included, and will be replaced with a new massive battle Unreal Warfare mode. Other gametypes - Deathmatch, Capture The Flag, Team Deathmatch and Onslaught will remain intact. There will be also changes in weaponry and equipment - the Bio Rifle will be merged into Canister Gun, with the Mine Layer and Grenade Launcher. Link Gun and the mysterious AVRiL weapon will be added. The Minigun will be replaced with Unreal's Stinger. A number of vechicles, as well as improved AI, voice recognition and focus on gameplay has been also confirmed.
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 digg this super bookmark
Basic Server Side Coding.
Welcome back!
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.
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.
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!
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
}
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.
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.
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.
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.
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!
exec function SpawnCube()
{
local class CubeClass;
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!
Only registered members can share their thoughts. So come on! Join the Mod DB community today (totally free) and do things you never thought possible.
That font size makes my eyes hurt. :-(
It was larger (it was formatted the same way as all the others). It must have been resized when authed 0_o
In fact no, it shrink purely at random when saved. God this text editor is buggy...
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
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;
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?
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,,, );
}
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=......."
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 :)
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
I think the UT compiler takes out any empty spaces, so <159> in your text editor may not be <159> to the compiler.
By the by, thanks for all the tutorials Ambershee. A+ stuff.
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'));