I was busy with the NOWA-Engine all the time and I thought, why not posting some new features
I created a 2,5 Jump'N'Run test scenario in NOWA-Design with ragdoll activation on the fly. The whole character movement is completely implemented in lua script and can be changed at runtime.
Code: Select all
module("PrehistoricLax_0", package.seeall);
-- Enums
NONE, UP, DOWN, LEFT, RIGHT = 0, 1, 2, 3, 4
playerController = nil;
backgroundMusic = nil;
walkSound = nil;
jumpSound = nil;
smokeParticle = nil;
animationBlender = nil;
characterSliderMaterial = nil;
leftFoot = nil;
rightFoot = nil;
leftFootOriginOrientation = nil;
rightFootOriginOrientation = nil;
directionMove = Vector3.ZERO;
direction = NONE;
directionChanged = false;
oldDirection = NONE;
isJumping = false;
boringTimer = 5;
noMoveTimer = 0;
jumpForce = 0;
inAir = false;
isAttacking = false;
tryJump = false;
highFalling = false;
jumpKeyPressed = false;
jumpCount = 0;
canDoubleJump = false;
walkCount = 0;
isOnRope = false;
groundedOnce = true;
duckedOnce = false;
acceleration = 0.5;
oldPositionY = 0;
relativeHeight = 0;
-- later control double jump via attributes component
doubleJump = true;
runAfterWalkTime = 3;
timeSinceLastPush = 1;
canPush = true;
PrehistoricLax_0 = {};
PrehistoricLax_0["connect"] = function(gameObject)
local mainGameObject = GameObjectController:getGameObjectFromId(MAIN_GAMEOBJECT_ID);
backgroundMusic = mainGameObject:getSimpleSoundComponent(0);
backgroundMusic:setActivated(true);
timeSinceLastPush = 1;
end
PrehistoricLax_0["disconnect"] = function(gameObject)
backgroundMusic:setActivated(false);
gameObject:getPlayerController():getPhysicsRagDollComponent():setState("Inactive");
end
WalkState = { };
WalkState["enter"] = function(gameObject)
playerController = gameObject:getPlayerControllerJumpNRunLuaComponent();
walkSound = playerController:getOwner():getSimpleSoundComponent(0);
jumpSound = playerController:getOwner():getSimpleSoundComponent(1);
smokeParticle = playerController:getOwner():getParticleUniverseComponent(0);
smokeParticle:setActivated(false);
--smokeParticle:setPlaySpeed(10);
smokeParticle:setPlayTimeMS(50);
--smokeParticle:setScale(Vector3(0.05, 0.05, 0.05));
walkSound:setActivated(false);
jumpSound:setActivated(false);
walkSound:setPitch(0.65);
walkSound:setVolume(15);
jumpSound:setVolume(50);
animationBlender = playerController:getAnimationBlender();
local mainGameObject = GameObjectController:getGameObjectFromId(MAIN_GAMEOBJECT_ID);
characterSliderMaterial = mainGameObject:getPhysicsMaterialComponent(2);
if (animationBlender ~= nil) then
log("enter: animationBlender ok");
else
log("enter: animationBlender nil");
end
animationBlender:registerAnimation(AnimationBlender.ANIM_IDLE_1, "Idle");
animationBlender:registerAnimation(AnimationBlender.ANIM_IDLE_2, "Greet");
animationBlender:registerAnimation(AnimationBlender.ANIM_IDLE_3, "Bored");
animationBlender:registerAnimation(AnimationBlender.ANIM_WALK_NORTH, "Walk");
animationBlender:registerAnimation(AnimationBlender.ANIM_WALK_SOUTH, "Walk_backwards");
animationBlender:registerAnimation(AnimationBlender.ANIM_WALK_WEST, "Walk");
animationBlender:registerAnimation(AnimationBlender.ANIM_WALK_EAST, "Walk");
animationBlender:registerAnimation(AnimationBlender.ANIM_JUMP_START, "Jump2");
animationBlender:registerAnimation(AnimationBlender.ANIM_JUMP_WALK, "Jump");
animationBlender:registerAnimation(AnimationBlender.ANIM_HIGH_JUMP_END, "Land");
animationBlender:registerAnimation(AnimationBlender.ANIM_JUMP_END, "Land2");
animationBlender:registerAnimation(AnimationBlender.ANIM_FALL, "Falling");
animationBlender:registerAnimation(AnimationBlender.ANIM_RUN, "Run");
animationBlender:registerAnimation(AnimationBlender.ANIM_SNEAK, "Take_damage");
animationBlender:registerAnimation(AnimationBlender.ANIM_DUCK, "Land2");
animationBlender:registerAnimation(AnimationBlender.ANIM_HALT, "Halt");
animationBlender:init(AnimationBlender.ANIM_IDLE_1, true);
boringTimer = 0;
noMoveTimer = 0;
jumpForce = 15;
end
WalkState["execute"] = function(gameObject, dt)
if (noMoveTimer > 0.1) then
playerController:setMoveWeight(0);
playerController:setJumpWeight(0);
noMoveTimer = noMoveTimer - dt;
elseif (noMoveTimer < 0.1) then
-- Call this only once, hence noMoveTimer = 0.1
playerController:setMoveWeight(1);
playerController:setJumpWeight(1);
noMoveTimer = 0.1;
end
local tempSpeed = 0;
local tempAnimationSpeed = playerController:getAnimationSpeed();
local heading = playerController:getOrientation():getYaw();
local height = playerController:getHeight();
local onSlider = false;
local onTrampolin = false;
local atObstacle = false;
local keyDirection = Vector3.ZERO;
directionMove = Vector3.ZERO;
if (height <= 0) then
inAir = true;
else
inAir = height > 0.4;
end
-- Store the old direction, the check later whether a direction changed or not
oldDirection = direction;
-- Store the old position y, to check later the relative height
if (inAir == false) then
oldPositionY = playerController:getPhysicsComponent():getPosition().y;
end
relativeHeight = playerController:getPhysicsComponent():getPosition().y - oldPositionY;
-- Set omega by default directly to zero, else there is an ugly small rotation...
playerController:getPhysicsComponent():applyAngularImpulse(Vector3.ZERO);
--log("In air: " .. (inAir and 'true' or 'false')); -- inAir is bool and somehow cannot be printed
--log("Height: " .. height);
--log("RelativeHeight: " .. relativeHeight);
------------------------------------------Ray Checks----------------------------------------------------
-- Check what is below the player
local hitGameObjectBelow = playerController:getHitGameObjectBelow();
if (hitGameObjectBelow ~= nil) then
if (hitGameObjectBelow:getCategory() == "Slider") then
if (inAir == false) then
onSlider = true;
local horizontalSlider = hitGameObjectBelow:getJointActiveSliderComponent();
if (horizontalSlider:getPin().x == 1) then
directionMove = horizontalSlider:getPin() * horizontalSlider:getCurrentDirection() * horizontalSlider:getMoveSpeed();
end
end
elseif (hitGameObjectBelow:getCategory() == "Trampolin") then
onTrampolin = true;
end
end
-- Check what is in front of the player
local hitGameObjectFront = playerController:getHitGameObjectFront();
--log("hitGameObjectFront: ");
if (hitGameObjectFront ~= nil) then
walkCount = 0;
if (inAir == true) then
atObstacle = true;
end
--log("hitGameObjectFront: " .. hitGameObjectFront:getName());
-- Halt when its a static collision object
if (hitGameObjectFront:getPhysicsArtifactComponent() ~= nil) then
playerController:setMoveWeight(0);
playerController:getPhysicsComponent():setVelocity(playerController:getPhysicsComponent():getVelocity() * Vector3(0, 1, 0));
end
end
-- Check what is above the player
local hitGameObjectAbove = playerController:getHitGameObjectUp();
--log("hitGameObjectFront: ");
if (hitGameObjectAbove ~= nil) then
walkCount = 0;
playerController:setMoveWeight(0);
playerController:getPhysicsComponent():setVelocity(playerController:getPhysicsComponent():getVelocity() * Vector3(0, 1, 0));
atObstacle = true;
end
-----------------------------------------Handle idle--------------------------------------------------
-- If no key is pressed or player is not able to move (moveWeight = 0), go to idle state
if ((playerController:getMoveWeight() == 0) or (InputDeviceModule:isActionDown(NOWA_A_UP) == false and InputDeviceModule:isActionDown(NOWA_A_DOWN) == false
and InputDeviceModule:isActionDown(NOWA_A_LEFT) == false and InputDeviceModule:isActionDown(NOWA_A_RIGHT) == false
and jumpKeyPressed == false and isJumping == false)) then
walkCount = 0;
keyDirection = Vector3.ZERO;
if (animationBlender:isAnimationActive(AnimationBlender.ANIM_IDLE_1) == false and inAir == false) then
animationBlender:blend(AnimationBlender.ANIM_IDLE_1, AnimationBlender.BlendWhileAnimating, 0.2, true);
end
tempAnimationSpeed = playerController:getAnimationSpeed() * 0.5;
-- Attention: Due to rotation of 90 degree at once, commented this out
--direction = NONE;
-- No acceleration, move with usual constant speed
if (playerController:getAcceleration() == 0) then
acceleration = 1;
else
acceleration = acceleration - playerController:getAcceleration() * dt;
if (acceleration <= 0.5) then
acceleration = 0.5;
end
end
boringTimer = boringTimer + dt;
if (inAir == true) then
boringTimer = 0;
end
-- If the user does nothing for 10 seconds, choose a random boring animation state
if (boringTimer >= 10) then
boringTimer = 0;
local id = MathHelper:getRandomNumberInt(0, 3);
local animID;
if (0 == id) then
animID = AnimationBlender.ANIM_IDLE_1;
elseif (1 == id) then
animID = AnimationBlender.ANIM_IDLE_2;
else
animID = AnimationBlender.ANIM_IDLE_3;
end
if (animationBlender:hasAnimation(animID) and animationBlender:isComplete()) then
tempAnimationSpeed = playerController:getAnimationSpeed() * 0.5;
animationBlender:blend(animID, AnimationBlender.BlendWhileAnimating, 0.2, true);
end
end
if (playerController:isIdle() and playerController:getPhysicsComponent():getHasBuoyancy() == false) then
playerController:getPhysicsComponent():setVelocity(playerController:getPhysicsComponent():getVelocity() * Vector3(0, 1, 0));
end
end
-------------------------------------------Handle movement--------------------------------
if (InputDeviceModule:isActionDown(NOWA_A_LEFT) or InputDeviceModule:isActionDown(NOWA_A_RIGHT) and atObstacle == false) then
boringTimer = 0;
-- walk if left or right is being pressed
boringTimer = 0;
local animId = AnimationBlender.ANIM_NONE;
-- JumpNRun 2D treatement: Rotate til 90 degree at once
if (InputDeviceModule:isActionDown(NOWA_A_LEFT)) then
if (runAfterWalkTime > 0 and inAir == false) then
walkCount = walkCount + dt;
end
-- Walked long enough without pause : run
if (walkCount >= runAfterWalkTime) then
animId = AnimationBlender.ANIM_RUN;
tempSpeed = playerController:getPhysicsComponent():getMaxSpeed() * playerController:getMoveWeight();
tempAnimationSpeed = tempAnimationSpeed * 0.5;
else
tempSpeed = playerController:getPhysicsComponent():getSpeed() * playerController:getMoveWeight();
animId = AnimationBlender.ANIM_WALK_NORTH;
end
direction = LEFT;
keyDirection = Vector3(-1, 0, 0);
elseif (InputDeviceModule:isActionDown(NOWA_A_RIGHT)) then
if (runAfterWalkTime > 0 and inAir == false) then
walkCount = walkCount + dt;
end
-- Walked long enough without pause : run
if (walkCount >= runAfterWalkTime) then
animId = AnimationBlender.ANIM_RUN;
tempSpeed = playerController:getPhysicsComponent():getMaxSpeed() * playerController:getMoveWeight();
tempAnimationSpeed = tempAnimationSpeed * 0.5;
else
tempSpeed = playerController:getPhysicsComponent():getSpeed() * playerController:getMoveWeight();
animId = AnimationBlender.ANIM_WALK_NORTH;
end
direction = RIGHT;
keyDirection = Vector3(1, 0, 0);
end
directionMove = keyDirection * tempSpeed * acceleration;
-- If player is stuck play corresponding animation
--if (playerController:getMoveWeight() == 0) then
-- animId = AnimationBlender.ANIM_HALT;
--end
if (animationBlender:isAnimationActive(animId) == false
and (animationBlender:isComplete() or animationBlender:isAnimationActive(AnimationBlender.ANIM_IDLE_1)
or animationBlender:isAnimationActive(AnimationBlender.ANIM_IDLE_2)
or animationBlender:isAnimationActive(AnimationBlender.ANIM_IDLE_3))
and playerController:getPhysicsComponent():getHasBuoyancy() == false
and inAir == false and jumpKeyPressed == false and isJumping == false and playerController:getMoveWeight() == 1 and InputDeviceModule:isActionDown(NOWA_A_UP) == false) then
tempAnimationSpeed = playerController:getAnimationSpeed();
-- 0.02f: Immediately blend to walk
---------------------------------------------------------------------------------------------------
animationBlender:blend(animId, AnimationBlender.BlendWhileAnimating, 0.2, true);
---------------------------------------------------------------------------------------------------
end
elseif (InputDeviceModule:isActionDown(NOWA_A_DUCK)) then
boringTimer = 0;
if (animationBlender:isAnimationActive(AnimationBlender.ANIM_DUCK) == false and duckedOnce == false) then
duckedOnce = true;
animationBlender:blend(AnimationBlender.ANIM_DUCK, AnimationBlender.BlendWhileAnimating, 0.1, false);
end
if (animationBlender:getTimePosition() >= animationBlender:getLength() - 0.3) then
animationBlender:setTimePosition(0.7);
end
elseif (InputDeviceModule:isActionDown(NOWA_A_DUCK) == false and duckedOnce == true) then
boringTimer = 0;
duckedOnce = false;
end
---------------------------------Player Rotation---------------------------------------------------------------
-- If the old direction does not match the current direction
-- the player changed direction, but this only set the direction change to true, never to false, which is done, after a 90 degree turn has been accomplished
if (oldDirection ~= direction) then
directionChanged = true;
walkCount = 0;
end
-- If the player changed direction, rotate the player from x to -x and vice version (+- 180 degree), after that directionChanged is set to false
if (directionChanged == true) then
boringTimer = 0;
local yawAtSpeed = 0;
local currentDegree = playerController:getPhysicsComponent():getOrientation():getYaw():valueDegrees();
if (direction == RIGHT) then
yawAtSpeed = playerController:getRotationSpeed();
if (currentDegree >= 90) then
directionChanged = false;
yawAtSpeed = 0;
playerController:getPhysicsComponent():setOrientation(Quaternion(Degree(90), Vector3.UNIT_Y));
end
elseif (direction == LEFT) then
yawAtSpeed = -playerController:getRotationSpeed();
if (currentDegree <= -90) then
directionChanged = false;
yawAtSpeed = 0;
playerController:getPhysicsComponent():setOrientation(Quaternion(Degree(-90), Vector3.UNIT_Y));
end
end
playerController:getPhysicsComponent():applyAngularImpulse(Vector3(0, yawAtSpeed, 0));
else
-- player should not rotate without intention
if (direction == LEFT) then
playerController:getPhysicsComponent():setOrientation(Quaternion(Degree(-90), Vector3.UNIT_Y));
else
playerController:getPhysicsComponent():setOrientation(Quaternion(Degree(90), Vector3.UNIT_Y));
end
end
-- Prevent, that the player does bounce after falling
if (inAir == false and playerController:getPhysicsComponent():getVelocity().y < -2 and onTrampolin == false and onSlider == false and isJumping == false) then
playerController:getPhysicsComponent():setVelocity(directionMove);
else
playerController:getPhysicsComponent():setVelocity(playerController:getPhysicsComponent():getVelocity() * Vector3(0, 1, 0) + directionMove);
if (inAir == false and direction ~= NONE) then
-- walkSound:setSecondOffset(2);
walkSound:setPitch(1);
walkSound:setVolume(55);
walkSound:setVelocity(playerController:getPhysicsComponent():getVelocity());
-----------------------------walkSound:setActivated(true);
-- walkSound:setRolloffFactor()
end
end
---------------------------------Ragdoll Test------------------------------------------------------------------
if timeSinceLastPush >= 0 then
timeSinceLastPush = timeSinceLastPush - dt;
end
-- Test
if timeSinceLastPush <= 0 then
if (InputDeviceModule:isKeyDown(KeyEvent.KC_T) and canPush == true) then
timeSinceLastPush = 1;
-- Change to rag doll state, so that no rays etc. are calculated and player cannot move for 5 seconds
playerController:getStateMachine():changeState(RagDollState);
go = GameObjectController:getGameObjectFromId(989094932);
go:getPhysicsRagDollComponent():setState("Ragdolling");
canPush = false;
end
else
canPush = true;
end
-----------------------------------------------Handle Jump---------------------------------------------------
jumpKeyPressed = false;
if (InputDeviceModule:isActionDown(NOWA_A_JUMP)) then
boringTimer = 0;
if (canDoubleJump == true) then
if (jumpCount == 0) then
jumpCount = jumpCount + 1;
-- When a double jump is made, it must be made in air, else if the player presses the jump key immediately a second time, the player will jump to high
elseif (relativeHeight > 3) then
jumpCount = jumpCount + 1;
end
canDoubleJump = false;
if (jumpCount > 2) then
jumpCount = 0;
end
end
if (tryJump == false) then
jumpKeyPressed = true;
end
else
-- In order that the player does not jump again and again if the jump key is hold, only set isJumping to false if the player released the jump key
-- so that he can jump if jump key is pressed again
tryJump = false;
isJumping = false;
canDoubleJump = true;
end
if (inAir == false and jumpKeyPressed == true and isJumping == false) then
if (animationBlender:isAnimationActive(AnimationBlender.ANIM_JUMP_START) and animationBlender:isComplete()) then
else
animationBlender:blend(AnimationBlender.ANIM_JUMP_START, AnimationBlender.BlendSwitch, 0.2, false); -- do not know why, but this must stand here, else sometimes instead of jump, the walk anim is executed
animationBlender:blend(AnimationBlender.ANIM_JUMP_START, AnimationBlender.BlendWhileAnimating, 0.2, false);
end
jumpSound:setActivated(true);
boringTimer = 0;
elseif (inAir == true and playerController:getPhysicsComponent():getVelocity().y < -2 and onSlider == false) then
jumpCount = 0;
if (animationBlender:isAnimationActive(AnimationBlender.ANIM_FALL) == false) then
animationBlender:blend(AnimationBlender.ANIM_FALL, AnimationBlender.BlendWhileAnimating, 0.2, true);
end
jumpKeyPressed = false;
end
if (inAir == false and groundedOnce == false) then
boringTimer = 0;
-- Play only once!
walkSound:setPitch(0.35);
walkSound:setVolume(55);
walkSound:setActivated(true);
smokeParticle:setOffsetPosition(Vector3(0, -0.1, 1));
smokeParticle:setActivated(true);
groundedOnce = true;
end
if (inAir == true) then
groundedOnce = false;
end
if (playerController:getJumpWeight() == 1 and (jumpKeyPressed == true and (isJumping == false and inAir == false) or (doubleJump == true and jumpCount == 2))) then
boringTimer = 0;
if (playerController:getPhysicsComponent():getVelocity().y <= jumpForce * jumpCount) then
playerController:getPhysicsComponent():addImpulse(Vector3(0, jumpForce * playerController:getJumpWeight(), 0));
if (jumpCount >= 2) then
jumpCount = 0;
end
else
isJumping = true;
playerController:setJumpWeight(0);
end
end
-- If the player is on ground and not jumping and the jump key pressed
if (jumpKeyPressed == true and (inAir == false and isJumping == false)) then
tryJump = true;
end
animationBlender:addTime(dt * tempAnimationSpeed / animationBlender:getLength());
end
WalkState["exit"] = function(gameObject)
end
ragDollTime = 5;
RagDollState = { }
RagDollState["enter"] = function(gameObject)
playerController = gameObject:getPlayerControllerJumpNRunLuaComponent();
ragDollTime = 5;
playerController:getPhysicsRagDollComponent():setState("Ragdolling");
end
RagDollState["execute"] = function(gameObject, dt)
-- Let the ragdoll set the velocity last set from movement for 1 second
if (ragDollTime >= 4) then
playerController:getPhysicsComponent():setVelocity(playerController:getPhysicsComponent():getVelocity() * Vector3(0, 1, 0) + directionMove);
end
if (ragDollTime > 0) then
ragDollTime = ragDollTime - dt;
else
playerController:getStateMachine():changeState(WalkState);
end
end
RagDollState["exit"] = function(gameObject)
playerController:getPhysicsRagDollComponent():setState("Inactive");
end