void CrowdManager::OnSceneSet(Scene* scene) { // Subscribe to the scene subsystem update, which will trigger the crowd update step, and grab a reference // to the scene's NavigationMesh if (scene) { if (scene != node_) { LOGERROR("CrowdManager is a scene component and should only be attached to the scene node"); return; } SubscribeToEvent(scene, E_SCENESUBSYSTEMUPDATE, HANDLER(CrowdManager, HandleSceneSubsystemUpdate)); // Attempt to auto discover a NavigationMesh component (or its derivative) under the scene node NavigationMesh* navMesh = scene->GetDerivedComponent<NavigationMesh>(true); if (navMesh) { navigationMesh_ = navMesh; navigationMeshId_ = navMesh->GetID(); CreateCrowd(); SubscribeToEvent(navMesh->GetNode(), E_NAVIGATION_MESH_REBUILT, HANDLER(CrowdManager, HandleNavMeshChanged)); SubscribeToEvent(navMesh->GetNode(), E_COMPONENTREMOVED, HANDLER(CrowdManager, HandleNavMeshChanged)); } } else { UnsubscribeFromEvent(E_SCENESUBSYSTEMUPDATE); UnsubscribeFromEvent(E_NAVIGATION_MESH_REBUILT); UnsubscribeFromEvent(E_COMPONENTREMOVED); navigationMesh_ = 0; } }
void Navigation::AddOrRemoveObject() { // Raycast and check if we hit a mushroom node. If yes, remove it, if no, create a new one Vector3 hitPos; Drawable* hitDrawable; if (Raycast(250.0f, hitPos, hitDrawable)) { // The part of the navigation mesh we must update, which is the world bounding box of the associated // drawable component BoundingBox updateBox; Node* hitNode = hitDrawable->GetNode(); if (hitNode->GetName() == "Mushroom") { updateBox = hitDrawable->GetWorldBoundingBox(); hitNode->Remove(); } else { Node* newNode = CreateMushroom(hitPos); updateBox = newNode->GetComponent<StaticModel>()->GetWorldBoundingBox(); } // Rebuild part of the navigation mesh, then recalculate path if applicable NavigationMesh* navMesh = scene_->GetComponent<NavigationMesh>(); navMesh->Build(updateBox); if (currentPath_.Size()) navMesh->FindPath(currentPath_, jackNode_->GetPosition(), endPos_); } }
void Navigation::SetPathPoint() { Vector3 hitPos; Drawable* hitDrawable; NavigationMesh* navMesh = scene_->GetComponent<NavigationMesh>(); if (Raycast(250.0f, hitPos, hitDrawable)) { Vector3 pathPos = navMesh->FindNearestPoint(hitPos, Vector3(1.0f, 1.0f, 1.0f)); if (GetSubsystem<Input>()->GetQualifierDown(QUAL_SHIFT)) { // Teleport currentPath_.Clear(); jackNode_->LookAt(Vector3(pathPos.x_, jackNode_->GetPosition().y_, pathPos.z_), Vector3::UP); jackNode_->SetPosition(pathPos); } else { // Calculate path from Jack's current position to the end point endPos_ = pathPos; navMesh->FindPath(currentPath_, jackNode_->GetPosition(), endPos_); } } }
void UrhoQuickStart::GenerateNavMesh() { NavigationMesh* navMesh = scene_->CreateComponent<NavigationMesh>(); scene_->CreateComponent<Navigable>(); navMesh->SetPadding(Vector3(0.0f, 1.0f, 0.0f)); navMesh->SetAgentRadius(2.0f); navMesh->Build(); }
void UrhoQuickStart::HandleKeyDown(StringHash eventType, VariantMap& eventData) { using namespace KeyDown; int key = eventData[P_KEY].GetInt(); if (key == KEY_ESC) { Console* console = GetSubsystem<Console>(); if (console->IsVisible()) console->SetVisible(false); else engine_->Exit(); } else if (key == KEY_F1) { Console* console = GetSubsystem<Console>(); console->Toggle(); } if (key == KEY_F2) drawDebug_ = !drawDebug_; if (key == KEY_E) { Vector3 hitPos; Drawable* hitDrawable; NavigationMesh* navMesh = scene_->GetComponent<NavigationMesh>(); if (Raycast(250.0f, hitPos, hitDrawable)) { Vector3 pathPos = navMesh->FindNearestPoint(hitPos, Vector3(2.0f, 2.0f, 2.0f)); botScript_->AddPath(botNode_->GetWorldPosition(), pathPos); } } }
void Navigation::CreateScene() { ResourceCache* cache = GetSubsystem<ResourceCache>(); scene_ = new Scene(context_); // Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000) // Also create a DebugRenderer component so that we can draw debug geometry scene_->CreateComponent<Octree>(); scene_->CreateComponent<DebugRenderer>(); // Create scene node & StaticModel component for showing a static plane Node* planeNode = scene_->CreateChild("Plane"); planeNode->SetScale(Vector3(100.0f, 1.0f, 100.0f)); StaticModel* planeObject = planeNode->CreateComponent<StaticModel>(); planeObject->SetModel(cache->GetResource<Model>("Models/Plane.mdl")); planeObject->SetMaterial(cache->GetResource<Material>("Materials/StoneTiled.xml")); // Create a Zone component for ambient lighting & fog control Node* zoneNode = scene_->CreateChild("Zone"); Zone* zone = zoneNode->CreateComponent<Zone>(); zone->SetBoundingBox(BoundingBox(-1000.0f, 1000.0f)); zone->SetAmbientColor(Color(0.15f, 0.15f, 0.15f)); zone->SetFogColor(Color(0.5f, 0.5f, 0.7f)); zone->SetFogStart(100.0f); zone->SetFogEnd(300.0f); // Create a directional light to the world. Enable cascaded shadows on it Node* lightNode = scene_->CreateChild("DirectionalLight"); lightNode->SetDirection(Vector3(0.6f, -1.0f, 0.8f)); Light* light = lightNode->CreateComponent<Light>(); light->SetLightType(LIGHT_DIRECTIONAL); light->SetCastShadows(true); light->SetShadowBias(BiasParameters(0.0001f, 0.5f)); // Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance light->SetShadowCascade(CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f)); // Create some mushrooms const unsigned NUM_MUSHROOMS = 100; for (unsigned i = 0; i < NUM_MUSHROOMS; ++i) CreateMushroom(Vector3(Random(90.0f) - 45.0f, 0.0f, Random(90.0f) - 45.0f)); // Create randomly sized boxes. If boxes are big enough, make them occluders const unsigned NUM_BOXES = 20; for (unsigned i = 0; i < NUM_BOXES; ++i) { Node* boxNode = scene_->CreateChild("Box"); float size = 1.0f + Random(10.0f); boxNode->SetPosition(Vector3(Random(80.0f) - 40.0f, size * 0.5f, Random(80.0f) - 40.0f)); boxNode->SetScale(size); StaticModel* boxObject = boxNode->CreateComponent<StaticModel>(); boxObject->SetModel(cache->GetResource<Model>("Models/Box.mdl")); boxObject->SetMaterial(cache->GetResource<Material>("Materials/Stone.xml")); boxObject->SetCastShadows(true); if (size >= 3.0f) boxObject->SetOccluder(true); } // Create a NavigationMesh component to the scene root NavigationMesh* navMesh = scene_->CreateComponent<NavigationMesh>(); // Create a Navigable component to the scene root. This tags all of the geometry in the scene as being part of the // navigation mesh. By default this is recursive, but the recursion could be turned off from Navigable scene_->CreateComponent<Navigable>(); // Add padding to the navigation mesh in Y-direction so that we can add objects on top of the tallest boxes // in the scene and still update the mesh correctly navMesh->SetPadding(Vector3(0.0f, 10.0f, 0.0f)); // Now build the navigation geometry. This will take some time. Note that the navigation mesh will prefer to use // physics geometry from the scene nodes, as it often is simpler, but if it can not find any (like in this example) // it will use renderable geometry instead navMesh->Build(); // Create the camera. Limit far clip distance to match the fog cameraNode_ = scene_->CreateChild("Camera"); Camera* camera = cameraNode_->CreateComponent<Camera>(); camera->SetFarClip(300.0f); // Set an initial position for the camera scene node above the plane cameraNode_->SetPosition(Vector3(0.0f, 5.0f, 0.0f)); }
void Navigation::RecalculatePath() { NavigationMesh* navMesh = scene_->GetComponent<NavigationMesh>(); navMesh->FindPath(currentPath_, startPos_, endPos_); }