I prepared two things: a video showing some gameplay of the features developed so far and a demo about the grabbing/climbing code. For the latter I stripped down the game at the bare minimum to let you see the important parts. Still it's quite messy so hang up with me for some explanation in order to understand what's happening behind the curtains.
Here's the video:gs2.zip
It shows enemy AI, sword combat techniques (such as blocking, slashing, kicking), jumping-hanging-climbing, destructible entities (the enemy is destructible too!), revised bow physics, piercing arrows, bow combat.
A note about the bow/arrow physics. See how the arrows are piercing the objects and sticking in them. Sometimes it is buggy, yet it has improved a lot. I think it's a feature quite satisfying to see in a game like this. The bow physics is a mix between the original physX you could see in my previous video and some code I wrote to correct the arrow trajectory in order to fake the air viscosity and the mass offset. Practically I check for the vertical posVelocity to force the rotation of the arrow: if the arrow is going up it has to point upwards, if it is going down it has to point downward. Just that.
2) Here's the demo:grab_demo.zip
Look at onUpdate. The first part is what makes my character moving forward and backward, rotate and such. Then quite at the end there's this block:
if(climb)
{
//var myTrig = false;
if(climb_time < object.animationGetDuration("swing")/object.animationSpeed)
{
wantAnim = "swing";
}
else
{
wantAnim = "swing_idle";
if((keys.up) && (!ClimbUp))
{
ManageRealtimeSound("player_climb2");
object.animationSpeed = animSpeed *1.3;
ClimbUp = true;
object.physics.enable = true;
up_time = 0;
var seq = new Sequence("climbup", 0.6);
if( (objects.find(climb_obj).rot.eulerY > 90) || (objects.find(climb_obj).rot.eulerY < -90) )
{
seq.createKeyframe(0.2, "<prop id='pos' type='vector' tweenin='stepped' x='" + (objects.find(climb_obj).posWorld.x) + "' y='" + (objects.find(climb_obj).posWorld.y-1.5) + "' z='" + (objects.find(climb_obj).posWorld.z) + "'/>");
seq.createKeyframe(0.4, "<prop id='pos' type='vector' tweenin='stepped' x='" + (objects.find(climb_obj).posWorld.x) + "' y='" + (objects.find(climb_obj).posWorld.y) + "' z='" + (objects.find(climb_obj).posWorld.z-0.5) + "'/>");
seq.createKeyframe(0.6, "<prop id='pos' type='vector' tweenin='stepped' x='" + (objects.find(climb_obj).posWorld.x) + "' y='" + (objects.find(climb_obj).posWorld.y+1.2) + "' z='" + (objects.find(climb_obj).posWorld.z-1.5) + "'/>");
}
else
{
seq.createKeyframe(0.2, "<prop id='pos' type='vector' tweenin='stepped' x='" + (objects.find(climb_obj).posWorld.x) + "' y='" + (objects.find(climb_obj).posWorld.y-1.5) + "' z='" + (objects.find(climb_obj).posWorld.z) + "'/>");
seq.createKeyframe(0.4, "<prop id='pos' type='vector' tweenin='stepped' x='" + (objects.find(climb_obj).posWorld.x) + "' y='" + (objects.find(climb_obj).posWorld.y) + "' z='" + (objects.find(climb_obj).posWorld.z+0.5) + "'/>");
seq.createKeyframe(0.6, "<prop id='pos' type='vector' tweenin='stepped' x='" + (objects.find(climb_obj).posWorld.x) + "' y='" + (objects.find(climb_obj).posWorld.y+1.2) + "' z='" + (objects.find(climb_obj).posWorld.z+1.5) + "'/>");
}
object.sequencePlay(seq);
}
if((keys.down) && (!ClimbUp))
{
ManageRealtimeSound("player_climb1");
object.physics.enable = true;
climb = false;
climb_obj = "none";
falling_time = 0;
falling_damage = false;
falling = true;
}
}
if(ClimbUp)
{
wantAnim = "climb";
if( up_time >= 0.6 )
{
climb = false;
falling_time = 0;
falling_damage = false;
falling = false;
object.animationSpeed = animSpeed;
if( (objects.find(climb_obj).rot.eulerY > 90) || (objects.find(climb_obj).rot.eulerY < -90) )
{
facing_direction = 180;
moveState = "backward";
}
else
{
facing_direction = 0;
moveState = "forward";
}
climb_obj = "none";
}
up_time += system.timerDelta;
}
climb_time += system.timerDelta;
}
this part of the code is responsible for the climbing part, when you are hanging,then you decide if climb or let you fall using the up and down keys. I start explaining from here because it more simple to understand it as state in which the character hangs in the air, introduced by a introduction and an exit.
This is the hanging state. Physis is disabled. Pratically it starts the section playing the swing animation then idles on the swing_idle animation wainting for input. If you press the up key, the ClimbUp var is set to true, and a sequence is crated on the fly getting the rotation data from the climb spot object. My game is two dimensional so I have only two possible orientation on the Y axis 180 or zero. The coordinates of the climbing should get the player right on the platform in the animation time. Then physics is re-enabled.
But how do I get there? ok, look at the onCollision function. To get this result you have play a bit with the collision notification types in order to make the engine get only the collisions you want and forget about the rest. We are interested in the collisions with the yellow bricks.
function onCollision(objectId)
{
var myObj = objects.find(objectId);
if( (myObj.type == "climb_spot") && (system.timer - last_impact_time > 2) )
{
if( (objects.find(objectId).rot.eulerY > 90) || (objects.find(objectId).rot.eulerY < -90) )
{
if(facing_direction == 180)
{
Climb(objectId);
last_impact_time = system.timer;
}
}
else
{
if(facing_direction == 0)
{
Climb(objectId);
last_impact_time = system.timer;
}
}
}
myObj = null;
}
last_impact_time is set to avoid the character caching again the hanging spot right after it has fallen. You can see that the script checks the right orientation for the flayer (again, my game has only to direction) then pass the objectId name to a function called Climb.
The function Climb is the introduction I was referring to before. It disables physics and creates a sequence to place the character in the right position in respect to the climb_spot.
function Climb(objID)
{
if(climb_obj != objID)
{
climb_obj = objID;
object.physics.enable = false;
climb = true;
climb_time = 0;
up_time = 0;
ResetStates();
// object.pos = objects.find(objID).pos;
//create a sequence to make the player grab the climb spot
var seq = new Sequence("grabin", 0.2);
if( (objects.find(climb_obj).rot.eulerY > 90) || (objects.find(climb_obj).rot.eulerY < -90) )
{
facing_direction = 180;
moveState = "backward";
seq.createKeyframe(0.2, "<prop id='pos' type='vector' tweenin='stepped' x='" + (objects.find(objID).posWorld.x) + "' y='" + (objects.find(objID).posWorld.y-2.6) + "' z='" + (objects.find(objID).posWorld.z+0.5) + "'/>");
seq.createKeyframe(0.2, "<prop id='rot' type='quaternion' tweenin='linear' x='0' y='1' z='0' w='-4.371139E-08' />");
}
else
{
facing_direction = 0;
moveState = "forward";
seq.createKeyframe(0.2, "<prop id='pos' type='vector' tweenin='stepped' x='" + (objects.find(objID).posWorld.x) + "' y='" + (objects.find(objID).posWorld.y-2.6) + "' z='" + (objects.find(objID).posWorld.z-0.5) + "'/>");
seq.createKeyframe(0.2, "<prop id='rot' type='quaternion' tweenin='linear' x='0' y='0' z='0' w='1' />");
}
ManageRealtimeSound("player_climb1");
object.sequencePlay(seq);
}
}
the script set the climb variable to true and let the sequence start. The cycle is complete, we are in the climb section of the onUpdate function once again.
Hope you enjoy it.
|