Dear Community,
This is something I've wanted to accomplish for awhile!
In this video below and in my code I am giving you, I show you how to:
1. use a single button press to turn a skeletal mesh into a rolling move-able kactor ball and
2. with another button press, turn the kactor ball back into the normal skeletal mesh character!
3. I provide you with the code to control the pawn in kactor ball form
4. I provide you with the class you can use to make sure your kactor version of your pawn is properly lit to match the utpawn skeletal mesh component.
Video
Here's a video showing my simple algorithm at work
~~~
My Implementation
1. Upon button press the pawn is made invisible and is made to follow the kactor ball that is spawned, high above in the sky. Collision on pawn is turned off so that if it hits something it will not die.
2. In ball form, via playertick, the WASD keys are intercepted and sent over to the pawn class to move the ball. The pawn is repositioned every tick using move (not nearly as expensive as SetLocation which rehashes collision as per unreal wikia documentation.
3. Upon changing back to skeletal mesh form the kactor is destroyed, and normal collision restablished.
If you prefer to reduce memory modifications, instead of destroying the ball simpley disable it or put it in a container in the level somewhere out of sight, using staticmeshcomponent.setRBPosition()
~~~
Collision Solutions
The most complicated part of this is getting the collisions to work right as your is changing forms, so that the kactor ball and the skeletal mesh do not overlap and cause weirdness for each other
The next most complicated thing is the collision on your kactor ball version of your pawn.
If you make a static mesh version of your skeletal mesh ( with 3ds max for example), if you give that static mesh sphere collision in the UDK the sphere collision is not going to fit very well unless you perfectly shape your static mesh version of your skeletal mesh.
I found a simple solution though!
I use a standard engine sphere mesh for the collision, and hide it, and attach the static mesh version of the skeletal mesh as a component without any collision at all.
This way you can tailor the exact radius of the collision of your static mesh to look the way you want, whereas in the Static Mesh Editor in UDK you cannot easily resize the sphere collision of your static mesh
~~~
So The Solution
Do not give the static mesh version of your Skeletal Mesh any collision at all, and turn off “per poly” and leave on “uses simple box,line, etc” so that per poly is not activated
This way your static mesh will not interfere with the simple engine sphere collsion
~~~
How To Make Spherized Static Mesh Version of Skeletal Mesh
1. I recommend downloading the Student version of 3ds Max, which is free, from the autodesk website.
They are so very nice to offer this awesome software for free
http://students.autodesk.com/?nd=download_center
3ds Max Download From The Official Auto Desk Website
2. Then, if you were not the artist who made the skeletal mesh, EXPORT the skeletal mesh from UDK as an .fbx
3. Then IMPORT the .fbx file into 3ds max.
4. now you can use the container viewer to delete the entire skeleton of the skeletal mesh
5. use the modifier system of 3ds max, and systematically
a. use the modifier “mesh select”
b. apply things like Bend or Spherize, or the FFD 4x4x4 to manipulate the shape of the skeletal mesh parts
c. to do individual vertex corrects or move parts of the mesh to reposition feet or head etc, use the modifier “Edit Mesh” after using “mesh select”
In this way you can systematically manipulate the skeletal mesh into the shape you want!
This is the exact process I used to take my own skeletal mesh, shown in this video, and make the static mesh spherized version
~~~
Skeletal Controllers
If you look carefully at start of video, I actually manipulate the skeletal mesh into a position smiliar to the static mesh before hiding the pawn.
This is done with skeletal controllers.
I will plan on doing a whole tutorial on skeletal controllers at some point soon, but please do internet search for more info on skeletal controllers for now.
~~~
Code For You
Here's the basic setup you need to do something similar to what you see in the video in your own game!
Copy Right Policy
************************************************** **************
My CopyRight Policy
If you use part or all of my algorithms and code please
give credit where credit is due, by posting a link to my tutorial hub:
(right click and get link address, this is abbreviated link)
http://forums.epicgames.com/threads/...de-and-Videos)
Also post on this page so others see this code as well and can benefit as you have.
And of course I'd like to know that all the time I've spent preparng these tutorials
has helped you
♥
-Rama
************************************************** **************
~~~
The Kactor Ball Class That Uses Your Static Mesh
~~~
Your Pawn Class
This is where most of the code is, I've split it into parts for easy reference, but its all once class
The Global Variables
The Core Functions, turning into and out of ball form
The Ball Controls
They are always relative to camera, so whatever direction your player controller / camera is facing, the ball will respond with relative wasd commands just like player pawn.
If you are using a third person camera and the pc rotation is not what you are using, replace
with your third person camera rotation.
~~~
Player Controller Class
You will need to use key binding to get ahold of the WASD keys, see my other tutorials for more info on this
Rama
This is something I've wanted to accomplish for awhile!
In this video below and in my code I am giving you, I show you how to:
1. use a single button press to turn a skeletal mesh into a rolling move-able kactor ball and
2. with another button press, turn the kactor ball back into the normal skeletal mesh character!
3. I provide you with the code to control the pawn in kactor ball form
4. I provide you with the class you can use to make sure your kactor version of your pawn is properly lit to match the utpawn skeletal mesh component.
Video
Here's a video showing my simple algorithm at work
~~~
My Implementation
1. Upon button press the pawn is made invisible and is made to follow the kactor ball that is spawned, high above in the sky. Collision on pawn is turned off so that if it hits something it will not die.
2. In ball form, via playertick, the WASD keys are intercepted and sent over to the pawn class to move the ball. The pawn is repositioned every tick using move (not nearly as expensive as SetLocation which rehashes collision as per unreal wikia documentation.
3. Upon changing back to skeletal mesh form the kactor is destroyed, and normal collision restablished.
If you prefer to reduce memory modifications, instead of destroying the ball simpley disable it or put it in a container in the level somewhere out of sight, using staticmeshcomponent.setRBPosition()
~~~
Collision Solutions
The most complicated part of this is getting the collisions to work right as your is changing forms, so that the kactor ball and the skeletal mesh do not overlap and cause weirdness for each other
The next most complicated thing is the collision on your kactor ball version of your pawn.
If you make a static mesh version of your skeletal mesh ( with 3ds max for example), if you give that static mesh sphere collision in the UDK the sphere collision is not going to fit very well unless you perfectly shape your static mesh version of your skeletal mesh.
I found a simple solution though!
I use a standard engine sphere mesh for the collision, and hide it, and attach the static mesh version of the skeletal mesh as a component without any collision at all.
This way you can tailor the exact radius of the collision of your static mesh to look the way you want, whereas in the Static Mesh Editor in UDK you cannot easily resize the sphere collision of your static mesh
~~~
So The Solution
Do not give the static mesh version of your Skeletal Mesh any collision at all, and turn off “per poly” and leave on “uses simple box,line, etc” so that per poly is not activated
This way your static mesh will not interfere with the simple engine sphere collsion
~~~
How To Make Spherized Static Mesh Version of Skeletal Mesh
1. I recommend downloading the Student version of 3ds Max, which is free, from the autodesk website.
They are so very nice to offer this awesome software for free
http://students.autodesk.com/?nd=download_center
3ds Max Download From The Official Auto Desk Website
2. Then, if you were not the artist who made the skeletal mesh, EXPORT the skeletal mesh from UDK as an .fbx
3. Then IMPORT the .fbx file into 3ds max.
4. now you can use the container viewer to delete the entire skeleton of the skeletal mesh
5. use the modifier system of 3ds max, and systematically
a. use the modifier “mesh select”
b. apply things like Bend or Spherize, or the FFD 4x4x4 to manipulate the shape of the skeletal mesh parts
c. to do individual vertex corrects or move parts of the mesh to reposition feet or head etc, use the modifier “Edit Mesh” after using “mesh select”
In this way you can systematically manipulate the skeletal mesh into the shape you want!
This is the exact process I used to take my own skeletal mesh, shown in this video, and make the static mesh spherized version
~~~
Skeletal Controllers
If you look carefully at start of video, I actually manipulate the skeletal mesh into a position smiliar to the static mesh before hiding the pawn.
This is done with skeletal controllers.
I will plan on doing a whole tutorial on skeletal controllers at some point soon, but please do internet search for more info on skeletal controllers for now.
~~~
Code For You
Here's the basic setup you need to do something similar to what you see in the video in your own game!
Copy Right Policy
************************************************** **************
My CopyRight Policy
If you use part or all of my algorithms and code please
give credit where credit is due, by posting a link to my tutorial hub:
(right click and get link address, this is abbreviated link)
http://forums.epicgames.com/threads/...de-and-Videos)
Also post on this page so others see this code as well and can benefit as you have.
And of course I'd like to know that all the time I've spent preparng these tutorials
has helped you
♥
-Rama
************************************************** **************
~~~
The Kactor Ball Class That Uses Your Static Mesh
Code:
Class RamaSkeletalMeshToBall Extends KActorSpawnable Placeable; var float ballJumpHeight; var bool isFalling; var float fallingSensitivity; //vars made global for efficiency var float groundZ; var float ballradius; var float ballheight; var float ballBottomZ; Simulated Event PostBeginPlay() { super.postbeginplay(); //set gameinfo, this is my game info //enables me to access the instance of this class //after game starts YourGameInfo(WorldInfo.Game).RamaSkeletalMeshToBall = Self; //call the below again any time you change/update size of this Object //retrieves bounding info and puts in these two vars Self.GetBoundingCylinder(ballheight, ballradius); //SET MASS setMass(0.7); //for tracking RB collisions Self.StaticMeshComponent.ScriptRigidBodyCollisionThreshold = 1; } //Rama's Kactor Set Mass Function //see other tutorials on this at // http://forums.epicgames.com/threads/925562-Ever-New-Joy-Tutorial-Hub-(Code-and-Videos) function setMass(float newMass) { local RB_BodySetup rbs; //invalid values if (newMass <= 0) return; rbs = new Class'RB_BodySetup'; rbs.MassScale = newMass; ( Self.StaticMeshComponent.GetRootBodyInstance() ).UpdateMassProperties(rbs); } //My Own Jump Algorithm Part 1 function jump() { local vector v; //if ball is not falling if (isFalling) return; v.z = ballJumpHeight; StaticMeshComponent.AddImpulse(v); } //My Own Jump Algorithm Part 2 function checkIsFalling() { local vector hitLoc; local vector hitNorm; local Actor a; //look straight down for aactor //do a trace from ball directly down to ground below; a = Self.trace(hitLoc, hitNorm, Location + Vect(0, 0, -1) * 500, Location ); //==== Actor found by Trace? ========== if ( a == none) { isFalling = true; return; } //============= //store the height of the ground that is directly below ball groundZ = hitLoc.z; //Get Bottom Z value of dimensions of this object //Location.z calculates from center of object, //we need the bottom z-value of object, where it would touch ground. //this calculation may vary depending on shape of object. //I've only tested it as working perfectly with a sphere-shaped Actor ballBottomZ = abs(Location.z - ballRadius / 2); //if the bottom of object is not close enough to the ground below in z-axis if (abs(ballBottomZ - groundZ) > fallingSensitivity ) { isFalling = true; } else isFalling = false; } Simulated Function tick( Float DeltaTime ) { super.tick(deltatime); checkIsFalling(); } defaultproperties { //Light Environment to be like Skeletal Mesh UT Pawn's Light Environment Begin Object Class=DynamicLightEnvironmentComponent Name=joyLightEnv bCastShadows = true bSynthesizeSHLight = true //higher performance cost ModShadowFadeoutExponent = 24 End Object Components.Add(joyLightEnv) //The visible component, your Skeletal Mesh Made Into Ball-ish Shape Begin Object Class=StaticMeshComponent Name=MetalWarriorSphereCollisionComp //so the lighting matches skeletal mesh LightEnvironment = joyLightEnv CastShadow=true bCastDynamicShadow = true //make sure it is visible to owner bOwnerNoSee=false //for asymetric scaling //scale3d=(x=1.7,y=1.7,z=1.7) StaticMesh = StaticMesh'yourStaticMeshOfSkeletalmesh' //no collision BlockNonZeroExtent =false BlockZeroExtent =false BlockActors =false CollideActors =false End Object Components.Add(MetalWarriorSphereCollisionComp) //Invisible Actual Collision Begin Object Name=StaticMeshComponent0 //scale scale3d = (x = 0.33, y = 0.33, z = 0.33) //mesh StaticMesh = StaticMesh'EngineMeshes.Sphere' //RB Collisions bNotifyRigidBodyCollision = true //hide HiddenGame = true End Object isFalling = false bCollideActors = true //this var may also vary , customize as needed fallingSensitivity = 60 //Jump Speed ballJumpHeight = 3733 //customize to taste, change set mass value in Post Begin Play as needed }
Your Pawn Class
This is where most of the code is, I've split it into parts for easy reference, but its all once class
The Global Variables
Code:
var yourControllerClass pc; var RamaSkeletalMeshToBall RamaSkeletalMeshToBall; var float ballSpeed; var float turnSpeed; var float ballAirControl; var float nearTopViewAccelFactor; var float ballMaxVelocitySize; var float ballCurVelocitySize; var Rotator r; var vector vc; var vector vc2;
Code:
//getting/setting WorldInfo so your classes can communicate function setVars() { //create this var in your gameinfo class PC = YourGameInfo(WorldInfo.Game).currentPlayer; } //Post Begin Play simulated function PostBeginPlay() { //very important line super.PostBeginPlay(); //set worldinfo //create this var in your gameinfo class YourGameInfo(WorldInfo.Game).currentPawn = Self; setTimer(0.1, false, 'setVars' ); } simulated function toggleRolling() { if (pc.isRolling) { exitJoyfulRoll(); } else enterJoyfulRoll(); } event TakeDamage(int DamageAmount, Controller EventInstigator, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser) { //no take damge while rolling //if you find pawn taking damage while entering/leaving rolling //use Settimer to extend how long "isRolling" state lasts so pawn //no take damage, //damage intercepted here if (pc.isRolling) return; super.TakeDamage(DamageAmount, EventInstigator, HitLocation, Momentum, DamageType, HitInfo, DamageCauser); } simulated function enterJoyfulRoll() { //moving? only allow switch when pawn is still if (vsize(velocity) > 4) return; //remove pawn collision so it doesnt hit stuff and die CollisionComponent = none; pc.isRolling = true; //move pawn vc = Location; move(vect(0, 0, 2048)); //spawn ball //you may not need to adjust the rotation //I've left this in so you can use it if needed r = rotation; //r.yaw -= 16384; //r.roll -= 8000; //important to spawn a little in front of skeletal mesh pawn location //or big excitement tends to happen //the 8 z units is if you want to raise ball up off ground when it spawns RamaSkeletalMeshToBall = Spawn(Class'RamaSkeletalMeshToBall',,, vc + Vector(rotation) * 32 + vect(0, 0, 8), r ) //make ball fall to ground after spawn RamaSkeletalMeshToBall.StaticMeshComponent.WakeRigidBody(); //hide pawn setHidden(true); } //================ simulated function exitJoyfulRoll() { //ball destroyed if (RamaSkeletalMeshToBall == none) return; //not while falling if (RamaSkeletalMeshToBall != none) if (RamaSkeletalMeshToBall.isFalling) return; //so pawn no longer being moved up in playertick() pc.isRolling = false; //destroy ball RamaSkeletalMeshToBall.destroy(); //show pawn and move to location //restore pawn collision CollisionComponent = CylinderComponent; //show pawn sethidden(false); //move pawn //much faster than SetLocation move(RamaSkeletalMeshToBall.Location - Location); //rotate pawn setRotation(pc.Rotation); //make sure pawn reaches ground SetPhysics(PHYS_Falling); }
They are always relative to camera, so whatever direction your player controller / camera is facing, the ball will respond with relative wasd commands just like player pawn.
If you are using a third person camera and the pc rotation is not what you are using, replace
Code:
pc.rotation
Code:
function ballMoveInternal() { //no ball if ( RamaSkeletalMeshToBall == none) return; //vc set by each of ballleft,right,etc. //ball dir vc2 = RamaSkeletalMeshToBall.staticmeshcomponent.getrootbodyinstance().GetUnrealWorldVelocity(); //going in same dir as told to move? if (vsize(normal(vc) - Normal(vc2)) < 1 ) { //make sure up to date ballCurVelocitySize = Vsize(vc2); //already reached top speed if (ballCurVelocitySize > ballMaxVelocitySize){ return; //=================== } } if (RamaSkeletalMeshToBall.isFalling) { ballAirControl = default.ballAirControl; } else { ballAirControl = 1; } //multiply impulse by speed and air control vc *= ballSpeed * ballAirControl; //apply impulse RamaSkeletalMeshToBall.StaticMeshComponent.AddImpulse(vc * deltaTimeBoostMultiplier); //additional impulse if changing directions //makes for smooth fun gameplay if (VSize(Normal(vc) - Normal(RamaSkeletalMeshToBall.StaticMeshComponent.GetRootBodyInstance().velocity)) > 1) { RamaSkeletalMeshToBall.StaticMeshComponent.AddImpulse(vc * turnSpeed * deltaTimeBoostMultiplier); } } function ballLeft() { r = pc.rotation; r.yaw -= 16384; //90 deg //move dir vc = Vector(r); ballMoveInternal(); } function ballRight() { r = pc.rotation; r.yaw += 16384; //90 deg vc = Vector(r); ballMoveInternal(); } function ballForward() { vc = Vector(pc.rotation); ballMoveInternal(); } function ballBack() { r = pc.rotation; r.yaw += 32768; //180 deg vc = Vector(r); ballMoveInternal(); } //to prevent unnecessary excitement //you know, like the ball flying across the level into outer space //and stuff like that function limitBallVelocityMax() { if (MetalWarriorBall == none) return; //if the ball figures out how to prematurely evolve into a super-powered spaceship //and tries to take off such that its velocity > 6000 //tell it to chill out for awhile longer before beginning its space voyage if (Vsize( MetalWarriorBall.staticmeshcomponent.getrootbodyinstance().GetUnrealWorldVelocity()) > 6000) { MetalWarriorBall.staticmeshcomponent.setRBLinearVelocity(vect(0, 0, 0)); } } Simulated Event Tick(float DeltaTime) { Super.Tick(DeltaTime); //=== Metal Warrior Ball ==== if(pc.isRolling){ limitBallVelocityMax(); } } defaultproperties { ballSpeed = 140 ballMaxVelocitySize = 2000 turnSpeed = 3.8 ballAirControl = 0.3 }
Player Controller Class
You will need to use key binding to get ahold of the WASD keys, see my other tutorials for more info on this
Code:
var bool keyisdownW; var bool keyisdownA; var bool keyisdownS; var bool keyisdownD; //create functions for keydownW and keyupW that set the boolean as true/false //do this for all 4 keys //then link these exec functions to your defaultinput.ini //see my third person camera tutorial for more details http://forums.epicgames.com/threads/942953-Video-How-to-Make-3rd-Person-Camera-Free-Roam-Camera-Mouse-Cursor-amp-Drag-Select?p=31030597#post31030597 //states var bool isRolling; Simulated Event PlayerTick(Float DT) { //============ SUPER ================== //veeeery important line super.playertick(DT); //====================================== //================== //Skeletal Mesh Ball Mode //=================== //ROLLING if(isRolling){ //not all elses to allow smoother turning if (keyisdownW) { pawn.ballForward(); } else if (keyisdownS) { pawn.ballBack(); } if (keyisdownA) { pawn.ballLeft(); } else if (keyisdownD) { pawn.ballRight(); } //make regular pawn follow ball //move is waaay faster than setlocation, setlocation causes collision rehash which causes //it to be visibly slower than move or moveSmooth pawn.move(YourGameinfo(Worldinfo.game).MetalWarriorBall.Location + vect(0, 0, 2048) - pawn.Location); } //END IS ROLLING }
Comment