Quaternion Camera::GetFaceCameraRotation(const Vector3& position, const Quaternion& rotation, FaceCameraMode mode, float minAngle) { if (!node_) return rotation; switch (mode) { case FC_ROTATE_XYZ: return node_->GetWorldRotation(); case FC_ROTATE_Y: { Vector3 euler = rotation.EulerAngles(); euler.y_ = node_->GetWorldRotation().EulerAngles().y_; return Quaternion(euler.x_, euler.y_, euler.z_); } case FC_LOOKAT_XYZ: { Quaternion lookAt; lookAt.FromLookRotation(position - node_->GetWorldPosition()); return lookAt; } case FC_LOOKAT_Y: case FC_LOOKAT_MIXED: { // Mixed mode needs true look-at vector const Vector3 lookAtVec(position - node_->GetWorldPosition()); // While Y-only lookat happens on an XZ plane to make sure there are no unwanted transitions or singularities const Vector3 lookAtVecXZ(lookAtVec.x_, 0.0f, lookAtVec.z_); Quaternion lookAt; lookAt.FromLookRotation(lookAtVecXZ); Vector3 euler = rotation.EulerAngles(); if (mode == FC_LOOKAT_MIXED) { const float angle = lookAtVec.Angle(rotation * Vector3::UP); if (angle > 180 - minAngle) euler.x_ += minAngle - (180 - angle); else if (angle < minAngle) euler.x_ -= minAngle - angle; } euler.y_ = lookAt.EulerAngles().y_; return Quaternion(euler.x_, euler.y_, euler.z_); } default: return rotation; } }
bool SpatialNode::LookAt(const Vector3& target, const Vector3& up, TransformSpace space) { SpatialNode* parentNode = SpatialParent(); Vector3 worldSpaceTarget; switch (space) { case TS_LOCAL: worldSpaceTarget = WorldTransform() * target; break; case TS_PARENT: worldSpaceTarget = !parentNode ? target : parentNode->WorldTransform() * target; break; case TS_WORLD: worldSpaceTarget = target; break; } Vector3 lookDir = worldSpaceTarget - WorldPosition(); // Check if target is very close, in that case can not reliably calculate lookat direction if (lookDir.Equals(Vector3::ZERO)) return false; Quaternion newRotation; // Do nothing if setting look rotation failed if (!newRotation.FromLookRotation(lookDir, up)) return false; SetWorldRotation(newRotation); return true; }
Quaternion Camera::GetFaceCameraRotation(const Vector3& position, const Quaternion& rotation, FaceCameraMode mode) { if (!node_) return rotation; switch (mode) { default: return rotation; case FC_ROTATE_XYZ: return node_->GetWorldRotation(); case FC_ROTATE_Y: { Vector3 euler = rotation.EulerAngles(); euler.y_ = node_->GetWorldRotation().EulerAngles().y_; return Quaternion(euler.x_, euler.y_, euler.z_); } case FC_LOOKAT_XYZ: { Quaternion lookAt; lookAt.FromLookRotation(position - node_->GetWorldPosition()); return lookAt; } case FC_LOOKAT_Y: { // Make the Y-only lookat happen on an XZ plane to make sure there are no unwanted transitions // or singularities Vector3 lookAtVec(position - node_->GetWorldPosition()); lookAtVec.y_ = 0.0f; Quaternion lookAt; lookAt.FromLookRotation(lookAtVec); Vector3 euler = rotation.EulerAngles(); euler.y_ = lookAt.EulerAngles().y_; return Quaternion(euler.x_, euler.y_, euler.z_); } } }
bool Node::LookAt(const Vector3& target, const Vector3& up) { Vector3 lookDir = target - GetWorldPosition(); // Check if target is very close, in that case can not reliably calculate lookat direction if (lookDir.Equals(Vector3::ZERO)) return false; Quaternion rotation; // Do nothing if setting look rotation failed if (!rotation.FromLookRotation(lookDir, up)) return false; SetRotation((parent_ == scene_ || !parent_) ? rotation : parent_->GetWorldRotation().Inverse() * rotation); return true; }
void Bird::HandleUpdate(StringHash eventType, VariantMap &eventData) { if (dead_ && GetPosition().y_ < -5.0f) Disable(); float timeStep = eventData[SceneUpdate::P_TIMESTEP].GetFloat(); if (sinceSpeciesSet_ < morphTime_){ Morph(); } sinceSpeciesSet_ += timeStep; if (sinceStateChange_ > stateDuration_){ switch (state_) { case BirdState::Flying: { SetState(BirdState::Landing); } break; case BirdState::Standing: { SetState(BirdState::Flying); } break; default: break; } } if(!(first_ && state_ == BirdState::Standing)) sinceStateChange_ += timeStep; switch (state_) { case BirdState::Flying: { Fly(timeStep); } break; case BirdState::Landing: { Land(timeStep); } break; case BirdState::Standing: { Stand(timeStep); } break; default: break; } //Move bird rootNode_->Translate(velocity_*timeStep, TS_WORLD); //Update rotation in accordance with the birds movement. if (velocity_.Length() > 0.01f){ Quaternion rotation = rootNode_->GetWorldRotation(); Quaternion aimRotation = rotation; aimRotation.FromLookRotation(velocity_); rootNode_->SetRotation(rotation.Slerp(aimRotation, 2.0f * timeStep * velocity_.Length())); } }
void Player::HandleSceneUpdate(StringHash eventType, VariantMap &eventData) { //Take the frame time step, which is stored as a double float timeStep = eventData[Update::P_TIMESTEP].GetFloat(); //Pulse and spin the counters' apples and hearts UpdateGUI(timeStep); //Only handle input when player is active if (!rootNode_->IsEnabled()) return; Input* input = GetSubsystem<Input>(); //Movement values Vector3 move = Vector3::ZERO; Vector3 moveJoy = Vector3::ZERO; Vector3 moveKey = Vector3::ZERO; float thrust = pilotMode_ ? 256.0f : 2323.0f; float maxSpeed = pilotMode_? 1.8f : 23.0f; //Firing values Vector3 fire = Vector3::ZERO; Vector3 fireJoy = Vector3::ZERO; Vector3 fireKey = Vector3::ZERO; //Read input if (input->GetJoystickByIndex(0)){ moveJoy = Vector3::RIGHT * input->GetJoystickByIndex(0)->GetAxisPosition(0) + Vector3::BACK * input->GetJoystickByIndex(0)->GetAxisPosition(1); fireJoy = Vector3::RIGHT * input->GetJoystickByIndex(0)->GetAxisPosition(2) + Vector3::BACK * input->GetJoystickByIndex(0)->GetAxisPosition(3); } moveKey = Vector3::LEFT * input->GetKeyDown(KEY_A) + Vector3::RIGHT * input->GetKeyDown(KEY_D) + Vector3::FORWARD * input->GetKeyDown(KEY_W) + Vector3::BACK * input->GetKeyDown(KEY_S); fireKey = Vector3::LEFT * (input->GetKeyDown(KEY_J) || input->GetKeyDown(KEY_KP_4)) + Vector3::RIGHT * (input->GetKeyDown(KEY_L) || input->GetKeyDown(KEY_KP_6)) + Vector3::FORWARD * (input->GetKeyDown(KEY_I) || input->GetKeyDown(KEY_KP_8)) + Vector3::BACK * (input->GetKeyDown(KEY_K) || input->GetKeyDown(KEY_KP_2) || input->GetKeyDown(KEY_KP_5)) + Quaternion(45.0f, Vector3::UP)*Vector3::LEFT * input->GetKeyDown(KEY_KP_7) + Quaternion(45.0f, Vector3::UP)*Vector3::RIGHT * input->GetKeyDown(KEY_KP_3) + Quaternion(45.0f, Vector3::UP)*Vector3::FORWARD * input->GetKeyDown(KEY_KP_9) + Quaternion(45.0f, Vector3::UP)*Vector3::BACK * input->GetKeyDown(KEY_KP_1); //Pick most significant input moveJoy.Length() > moveKey.Length() ? move = moveJoy : move = moveKey; fireJoy.Length() > fireKey.Length() ? fire = fireJoy : fire = fireKey; //Restrict move vector length if (move.Length() > 1.0f) move /= move.Length(); //Deadzone else if (move.Length() < 0.1f) move *= 0.0f; if (fire.Length() < 0.1f) fire *= 0.0f; else fire.Normalize(); //When in pilot mode if (pilotMode_){ //Apply movement Vector3 force = move * thrust * timeStep; if (rigidBody_->GetLinearVelocity().Length() < maxSpeed || (rigidBody_->GetLinearVelocity().Normalized() + force.Normalized()).Length() < 1.0f) { rigidBody_->ApplyForce(force); } //Update rotation according to direction of the player's movement. Vector3 velocity = rigidBody_->GetLinearVelocity(); Vector3 lookDirection = velocity + 2.0f*fire; Quaternion rotation = rootNode_->GetWorldRotation(); Quaternion aimRotation = rotation; aimRotation.FromLookRotation(lookDirection); rootNode_->SetRotation(rotation.Slerp(aimRotation, 7.0f * timeStep * velocity.Length())); //Update animation if (velocity.Length() > 0.05f){ animCtrl_->PlayExclusive("Resources/Models/WalkRelax.ani", 0, true, 0.15f); animCtrl_->SetSpeed("Resources/Models/WalkRelax.ani", velocity.Length()*2.3f); animCtrl_->SetStartBone("Resources/Models/WalkRelax.ani", "MasterBone"); } else { animCtrl_->PlayExclusive("Resources/Models/IdleRelax.ani", 0, true, 0.15f); animCtrl_->SetStartBone("Resources/Models/IdleRelax.ani", "MasterBone"); } // When in ship mode } else { //Update shield Quaternion randomRotation = Quaternion(Random(360.0f),Random(360.0f),Random(360.0f)); shieldNode_->SetRotation(shieldNode_->GetRotation().Slerp(randomRotation, Random(1.0f))); Color shieldColor = shieldMaterial_->GetShaderParameter("MatDiffColor").GetColor(); Color newColor = Color(shieldColor.r_ * Random(0.6f, 0.9f), shieldColor.g_ * Random(0.7f, 0.95f), shieldColor.b_ * Random(0.8f, 0.9f)); shieldMaterial_->SetShaderParameter("MatDiffColor", shieldColor.Lerp(newColor, Min(timeStep * 23.5f, 1.0f))); //Float ship_.node_->SetPosition(Vector3::UP *masterControl_->Sine(2.3f, -0.1f, 0.1f)); //Apply movement Vector3 force = move * thrust * timeStep; if (rigidBody_->GetLinearVelocity().Length() < maxSpeed || (rigidBody_->GetLinearVelocity().Normalized() + force.Normalized()).Length() < 1.0f) { rigidBody_->ApplyForce(force); } //Update rotation according to direction of the ship's movement. if (rigidBody_->GetLinearVelocity().Length() > 0.1f) rootNode_->LookAt(rootNode_->GetPosition()+rigidBody_->GetLinearVelocity()); //Shooting sinceLastShot_ += timeStep; if (fire.Length()) { if (sinceLastShot_ > shotInterval_) { Shoot(fire); } } } }
void player::update(Input* input,float timeStep) { Node* node_camera=globals::instance()->camera->GetNode(); { Vector3 moveDir=Vector3::ZERO; Vector3 moveDir_global=Vector3::ZERO; if(input->GetKeyDown('D')) moveDir+=Vector3::RIGHT*1; if(input->GetKeyDown('A')) moveDir-=Vector3::RIGHT*1; if(input->GetKeyDown('W')) moveDir+=Vector3::FORWARD*1; if(input->GetKeyDown('S')) moveDir-=Vector3::FORWARD*1; if(moveDir.Length()>0.1) body->SetFriction(0.4); else body->SetFriction(2.0); if(moveDir.Length()>0.5) moveDir.Normalize(); Vector3 vel=body->GetLinearVelocity()*Vector3(1,0,1); Quaternion rot=body->GetRotation(); static bool on_floor; Vector3 moveDir_world; { static bool at_wall; static int jumping=0; // 0 = not jumping, 1 = jumping, 2 = on_floor=false; float height=0; PhysicsRaycastResult result; Ray ray(node->GetPosition()+Vector3(0,1.0,0),Vector3::DOWN); globals::instance()->physical_world->SphereCast(result,ray,0.2,2,2); if(result.distance_<=2) on_floor=true; if(!on_floor) moveDir*=0.35; as_stand->AddTime(timeStep/2); as_walk->AddTime(timeStep*vel.Length()/1.5); as_run->AddTime(timeStep*vel.Length()/3); as_jump->AddTime(timeStep); as_reversing->AddTime(timeStep); as_stand->SetWeight(1.0-Clamp(vel.Length()/2.0,0.0,1.0)); as_stand->SetWeight(1.0); as_walk->SetWeight(Clamp(vel.Length()/2.0,0.0,1.0)); as_run->SetWeight(Clamp((vel.Length()-2)/2.0,0.0,1.0)); // maybe this should be done differently, but works for this game if(!on_floor) as_jump->SetWeight(as_jump->GetWeight()+timeStep*5); else { as_jump->SetWeight(0.0); static bool sound_step1_not_played=true; // not playing this animation cycle static bool sound_step2_not_played=true; if(as_run->GetTime()>0.1&&sound_step1_not_played&&as_reversing->GetWeight()<0.5) { sound_source1->Play(sound_step1); sound_step1_not_played=false; } if(as_run->GetTime()>0.6&&sound_step2_not_played&&as_reversing->GetWeight()<0.5) { sound_source2->Play(sound_step2); sound_step2_not_played=false; } if(as_run->GetTime()<0.1) { sound_step1_not_played=true; sound_step2_not_played=true; } } if(input->GetKeyDown(KEY_SPACE)&&jumping==false&&(on_floor)) jumping=1; // start jumping else if(!input->GetKeyDown(KEY_SPACE)) jumping=0; static float jump_force_applied=0; static const float max_jump_force_applied=100; moveDir_world=node->GetWorldRotation()*moveDir; if(moveDir_world.Angle(vel)>90&&vel.Length()>3&&on_floor) // indicate if direction change jump / side sommersault possible as_reversing->SetWeight(as_reversing->GetWeight()+timeStep*10); else as_reversing->SetWeight(0.0); if(jumping==1&&jump_force_applied<max_jump_force_applied) // jump higher if we are jumping and the limit has not been reached { if(jump_force_applied>max_jump_force_applied) { // do nothing if max jump force reached } else if(jump_force_applied+timeStep*800>max_jump_force_applied) { // I want to limit the jump height more exactly by limiting the force pumped into it and applieng the remaining rest here. Doesn't fully work yet. //float f=0;//(max_jump_force_applied-jump_force_applied)*timeStep*2000; //moveDir+=Vector3::UP*2*f; //moveDir_global=result.normal_*1*f; jump_force_applied+=timeStep*900; } else { float f=1; moveDir+=Vector3::UP*0.6*f; moveDir_global=result.normal_*0.3*f; jump_force_applied+=timeStep*800; } } if(jumping!=1) jump_force_applied=0; } float f=0.5; // for walking or sprinting if(input->GetKeyDown(KEY_SHIFT)) f=1.0; Quaternion quat; quat.FromLookRotation(node_camera->GetDirection()*Vector3(1,0,1),Vector3::UP); body->SetRotation(quat); float speed_old=vel.Length(); vel+=rot*moveDir*timeStep*4000*f/body->GetMass(); float speed_new=vel.Length(); if(speed_new>10*f&&speed_new>speed_old) // over limit. Don't increase speed further but make direction change possible. vel=vel.Normalized()*speed_old; body->SetLinearVelocity(Vector3(vel.x_,body->GetLinearVelocity().y_+(rot*moveDir*timeStep*4500/body->GetMass()).y_,vel.z_)); body->ApplyImpulse(moveDir_global*timeStep*4000); { auto vec_rot=body->GetLinearVelocity()*Vector3(1,0,1); float s=vec_rot.Length(); vec_rot.Normalize(); float yaw=asin(vec_rot.x_)*180/M_PI; if(vec_rot.z_<0) yaw=180-yaw; node_model->SetPosition(node->GetPosition()); if(s>1&&!camera_first_person) { node_model->SetDirection(Vector3::FORWARD); node_model->Yaw(yaw); } } { // physic raycast to avoid the player glitching through stuff when moving very fast Vector3 player_pos=body->GetPosition()+Vector3(0,1,0); PhysicsRaycastResult result; Ray ray(pos_last,player_pos-pos_last); float l=(player_pos-pos_last).Length(); if(l>0.5) { globals::instance()->physical_world->SphereCast(result,ray,0.2,l,2); if(result.distance_<=l) body->SetPosition(pos_last); pos_last=body->GetPosition()+Vector3(0,1,0); } } } IntVector2 mouseMove(0,0); if(!input->IsMouseVisible()) mouseMove=input->GetMouseMove(); camera_yaw+=mouseMove.x_*0.1; if(camera_yaw<0) camera_yaw+=360; if(camera_yaw>=360) camera_yaw-=360; camera_pitch+=mouseMove.y_*0.1; camera_pitch=Clamp(camera_pitch,-85.0f,85.0f); if(!camera_first_person) { camera_distance-=input->GetMouseMoveWheel(); camera_distance=Clamp(camera_distance,2.0f,50.0f); node_camera->SetPosition(node->GetPosition()); node_camera->SetDirection(Vector3::FORWARD); node_camera->Yaw(camera_yaw); node_camera->Translate(Vector3(0,1.6,0)); node_camera->Pitch(camera_pitch); PhysicsRaycastResult result; Ray ray(node_camera->GetPosition(),-node_camera->GetDirection()); globals::instance()->physical_world->SphereCast(result,ray,0.2,camera_distance,2); if(result.distance_<=camera_distance) node_camera->Translate(Vector3(0,0,-result.distance_+0.1)); else node_camera->Translate(Vector3(0,0,-camera_distance)); } else { node_camera->SetPosition(node_camera_pos->GetWorldPosition()); node_camera->SetDirection(Vector3::FORWARD); node_camera->Yaw(camera_yaw); node_camera->Pitch(camera_pitch); node_camera->Translate(Vector3(0,0,0.1)); node_model->SetDirection(Vector3::FORWARD); node_model->Yaw(camera_yaw); } node_light->SetPosition(node_camera_pos->GetWorldPosition()); node_light->Translate(Vector3(0.5,-0.9,0.3)); float yaw_360_correction=(camera_yaw+Random(-light_shaking,light_shaking))-light_yaw; if(yaw_360_correction>180) yaw_360_correction-=360; else if(yaw_360_correction<-180) yaw_360_correction+=360; light_yaw+=yaw_360_correction*(10*timeStep); light_pitch+=((camera_pitch+Random(-light_shaking,light_shaking))-light_pitch)*(10*timeStep); node_light->SetDirection(Vector3::FORWARD); node_light->Yaw(light_yaw); node_light->Pitch(light_pitch); }