// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #include #include #include static void createFogVolumeFadeEvent(SceneNode* node) { CString script = R"( density = 15 radius = 3.5 function update(event, prevTime, crntTime) node = event:getAssociatedSceneNodes():getAt(0) -- logi(string.format("Will fade fog for %s", node:getName())) fogComponent = node:getFirstFogDensityComponent() dt = crntTime - prevTime density = density - 4.0 * dt radius = radius + 0.5 * dt pos = node:getLocalOrigin() pos:setY(pos:getY() - 1.1 * dt) node:setLocalOrigin(pos) if density <= 0.0 or radius <= 0.0 then node:markForDeletion() else node:setLocalScale(Vec3.new(radius)) fogComponent:setDensity(density) end end function onKilled(event, prevTime, crntTime) -- Nothing end )"; ScriptEvent* event = SceneGraph::getSingleton().getEventManager().newEvent(-1, 10.0, script); event->addAssociatedSceneNode(node); } FpsCharacter::FpsCharacter(CString name) : SceneNode(name) { newComponent(); SceneNode* cam = SceneGraph::getSingleton().newSceneNode("FpsCharacterCam"); cam->setLocalTransform(Transform(Vec3(0.0f, 2.0f, 0.0f), Mat3::getIdentity(), Vec3(1.0f))); CameraComponent* camc = cam->newComponent(); camc->setPerspective(0.1f, 1000.0f, Renderer::getSingleton().getAspectRatio() * toRad(g_cvarGameFov), toRad(g_cvarGameFov)); addChild(cam); m_cameraNode = cam; SceneNode* shotgun = SceneGraph::getSingleton().newSceneNode("Shotgun"); shotgun->setLocalTransform(Transform(m_shotgunRestingPosition, Mat3(m_shotgunRestingRotation), Vec3(1.0f, 1.0f, 0.45f))); shotgun->newComponent()->setMeshFilename("Assets/sleevegloveLOW.001_893660395596b206.ankimesh"); shotgun->newComponent()->setMaterialFilename("Assets/arms_3a4232ebbd425e7a.ankimtl").setSubmeshIndex(0); shotgun->newComponent()->setMaterialFilename("Assets/boomstick_89a614a521ace7fd.ankimtl").setSubmeshIndex(1); cam->addChild(shotgun); m_shotgunNode = shotgun; } void FpsCharacter::frameUpdate([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime) { // Change FOV CameraComponent& camc = m_cameraNode->getFirstComponentOfType(); if(toRad(g_cvarGameFov) != camc.getFovY()) { camc.setFovX(Renderer::getSingleton().getAspectRatio() * toRad(g_cvarGameFov)); camc.setFovY(toRad(g_cvarGameFov)); } // Mouselook const Input& inp = Input::getSingleton(); const Vec2 mousePos = inp.getMousePosition(); if(mousePos != 0.0f) { Mat3 camRot = m_cameraNode->getLocalRotation(); Mat3 newRot(Euler(g_cvarGameMouseLookPower * mousePos.y(), g_cvarGameMouseLookPower * -mousePos.x(), 0.0f)); newRot = camRot * newRot; const Vec3 newz = newRot.getColumn(2).normalize(); const Vec3 newx = Vec3(0.0, 1.0, 0.0).cross(newz); const Vec3 newy = newz.cross(newx); newRot.setColumns(newx, newy, newz); newRot = newRot.reorthogonalize(); m_cameraNode->setLocalRotation(newRot); } // Movement { Vec3 moveVec(0.0); if(inp.getKey(KeyCode::kW)) { moveVec.z() += 1.0f; } if(inp.getKey(KeyCode::kA)) { moveVec.x() += 1.0f; } if(inp.getKey(KeyCode::kS)) { moveVec.z() -= 1.0f; } if(inp.getKey(KeyCode::kD)) { moveVec.x() -= 1.0f; } F32 jumpSpeed = 0.0f; if(inp.getKey(KeyCode::kSpace)) { jumpSpeed += m_jumpSpeed; } Bool crouchChanged = false; if(inp.getKey(KeyCode::kC)) { m_crouching = !m_crouching; crouchChanged = true; } if(moveVec != 0.0f || jumpSpeed != 0.0f || crouchChanged) { Vec3 dir; if(moveVec != 0.0f) { dir = -(m_cameraNode->getLocalRotation() * moveVec); dir.y() = 0.0f; dir = dir.normalize(); } F32 speed = m_walkingSpeed; if(inp.getKey(KeyCode::kLeftShift)) { speed *= 2.0f; } PlayerControllerComponent& playerc = getFirstComponentOfType(); playerc.setVelocity(speed, jumpSpeed, dir, m_crouching); } } // Animate gun back to resting position if(m_shotgunNode->getLocalOrigin() != m_shotgunRestingPosition) { const Vec3 gunPos = m_shotgunNode->getLocalOrigin(); Vec3 travelDir = m_shotgunRestingPosition - gunPos; const F32 remainlingDist = travelDir.length(); travelDir = travelDir.normalize(); const F32 speed = getRandomRange(0.2f, 0.4f); const F32 dist = speed * F32(crntTime - prevUpdateTime); if(dist >= remainlingDist) { m_shotgunNode->setLocalOrigin(m_shotgunRestingPosition); m_shotgunNode->setLocalRotation(Mat3(m_shotgunRestingRotation)); } else { m_shotgunNode->setLocalOrigin(travelDir * dist + gunPos); const F32 distFactor = dist / remainlingDist; const Quat crntAngle(m_shotgunNode->getLocalRotation()); const Quat restingAngle(m_shotgunRestingRotation); const Quat newAngle = crntAngle.slerp(restingAngle, distFactor); m_shotgunNode->setLocalRotation(Mat3(newAngle)); } } // Shooting if(inp.getMouseButton(MouseButton::kLeft) == 1) { fireShotgun(); const Vec3 newPosition(0.0f, getRandomRange(-0.05f, -0.03f), 0.15f); m_shotgunNode->setLocalOrigin(m_shotgunRestingPosition + newPosition); const Euler newRotation(getRandomRange(0.0_degrees, 10.0_degrees), getRandomRange(-10.0_degrees, 1.0_degrees), 0.0f); m_shotgunNode->setLocalRotation(Mat3(newRotation) * Mat3(m_shotgunRestingRotation)); } if(inp.getMouseButton(MouseButton::kRight) == 1) { fireGrenade(); } } void FpsCharacter::fireShotgun() { for(U32 i = 0; i < 8; ++i) { F32 spreadAngle = getRandomRange(-m_shotgunSpreadAngle / 2.0f, m_shotgunSpreadAngle / 2.0f); Mat3 randDirection(Axisang(spreadAngle, Vec3(1.0f, 0.0f, 0.0f))); spreadAngle = getRandomRange(-m_shotgunSpreadAngle / 2.0f, m_shotgunSpreadAngle / 2.0f); randDirection = randDirection * Mat3(Axisang(spreadAngle, Vec3(0.0f, 1.0f, 0.0f))); randDirection = m_cameraNode->getLocalRotation().getRotationPart() * randDirection; const Vec3 from = m_cameraNode->getWorldTransform().getOrigin().xyz(); const Vec3 to = from + -randDirection.getZAxis() * m_shotgunMaxLength; RayHitResult result; const Bool hit = PhysicsWorld::getSingleton().castRayClosestHit(from, to, PhysicsLayerBit::kStatic | PhysicsLayerBit::kMoving, result); if(hit && result.m_object->getType() == PhysicsObjectType::kBody) { PhysicsBody& body = static_cast(*result.m_object); const Bool isStatic = body.getMass() == 0.0f; if(isStatic) { // Create rotation const Vec3& zAxis = result.m_normal; Vec3 yAxis = Vec3(0, 1, 0.5); Vec3 xAxis = yAxis.cross(zAxis).normalize(); yAxis = zAxis.cross(xAxis); Mat3 rot; rot.setXAxis(xAxis); rot.setYAxis(yAxis); rot.setZAxis(zAxis); const Transform trf(result.m_hitPosition, rot, Vec3(1.0f, 1.0f, 1.0f)); // Create an obj SceneNode* bulletDecal = SceneGraph::getSingleton().newSceneNode(""); bulletDecal->setLocalTransform(trf); bulletDecal->setLocalScale(Vec3(0.1f, 0.1f, 0.3f)); DecalComponent* decalc = bulletDecal->newComponent(); decalc->loadDiffuseImageResource("Assets/bullet_hole_decal.ankitex", 1.0f); createDestructionEvent(bulletDecal, 10.0_sec); } else { const Vec3 force = -result.m_normal * m_shotgunForce; body.applyForce(force); } if(i == 0) { SceneNode* fogNode = SceneGraph::getSingleton().newSceneNode(""); FogDensityComponent* fogComp = fogNode->newComponent(); fogNode->setLocalScale(Vec3(2.1f)); fogComp->setDensity(15.0f); fogNode->setLocalOrigin(result.m_hitPosition); createDestructionEvent(fogNode, 10.0_sec); createFogVolumeFadeEvent(fogNode); } } } } void FpsCharacter::fireGrenade() { Transform camTrf = m_cameraNode->getWorldTransform(); const Vec3 newPos = camTrf.getOrigin().xyz() + camTrf.getRotation().getZAxis() * -3.0f; camTrf.setOrigin(newPos.xyz0()); SceneNode* grenade = SceneGraph::getSingleton().newSceneNode(""); BodyComponent& bodyc = grenade->getFirstComponentOfType(); bodyc.teleportTo(camTrf.getOrigin().xyz(), camTrf.getRotation().getRotationPart()); bodyc.applyForce(camTrf.getRotation().getZAxis().xyz() * -1200.0f, Vec3(0.0f, 0.0f, 0.0f)); }