// // Copyright (c) 2008-2015 the Urho3D project. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "SceneReplication.h" #include // UDP port we will use static const unsigned short SERVER_PORT = 2345; // Identifier for our custom remote event we use to tell the client which object they control static const StringHash E_CLIENTOBJECTID("ClientObjectID"); // Identifier for the node ID parameter in the event data static const StringHash P_ID("ID"); // Control bits we define static const unsigned CTRL_FORWARD = 1; static const unsigned CTRL_BACK = 2; static const unsigned CTRL_LEFT = 4; static const unsigned CTRL_RIGHT = 8; DEFINE_APPLICATION_MAIN(SceneReplication) SceneReplication::SceneReplication(Context* context) : Sample(context) { } void SceneReplication::Start() { // Execute base class startup Sample::Start(); // Create the scene content CreateScene(); // Create the UI content CreateUI(); // Setup the viewport for displaying the scene SetupViewport(); // Hook up to necessary events SubscribeToEvents(); } void SceneReplication::CreateScene() { scene_ = new Scene(context_); // Create scene content on the server only ResourceCache* cache = GetSubsystem(); // Create octree and physics world with default settings. Create them as local so that they are not needlessly replicated // when a client connects scene_->CreateComponent(LOCAL); scene_->CreateComponent(LOCAL); // All static scene content and the camera are also created as local, so that they are unaffected by scene replication and are // not removed from the client upon connection. Create a Zone component first for ambient lighting & fog control. Node* zoneNode = scene_->CreateChild("Zone", LOCAL); Zone* zone = zoneNode->CreateComponent(); zone->SetBoundingBox(BoundingBox(-1000.0f, 1000.0f)); zone->SetAmbientColor(Color(0.1f, 0.1f, 0.1f)); zone->SetFogStart(100.0f); zone->SetFogEnd(300.0f); // Create a directional light without shadows Node* lightNode = scene_->CreateChild("DirectionalLight", LOCAL); lightNode->SetDirection(Vector3(0.5f, -1.0f, 0.5f)); Light* light = lightNode->CreateComponent(); light->SetLightType(LIGHT_DIRECTIONAL); light->SetColor(Color(0.2f, 0.2f, 0.2f)); light->SetSpecularIntensity(1.0f); // Create a "floor" consisting of several tiles. Make the tiles physical but leave small cracks between them for (int y = -20; y <= 20; ++y) { for (int x = -20; x <= 20; ++x) { Node* floorNode = scene_->CreateChild("FloorTile", LOCAL); floorNode->SetPosition(Vector3(x * 20.2f, -0.5f, y * 20.2f)); floorNode->SetScale(Vector3(20.0f, 1.0f, 20.0f)); StaticModel* floorObject = floorNode->CreateComponent(); floorObject->SetModel(cache->GetResource("Models/Box.mdl")); floorObject->SetMaterial(cache->GetResource("Materials/Stone.xml")); RigidBody* body = floorNode->CreateComponent(); body->SetFriction(1.0f); CollisionShape* shape = floorNode->CreateComponent(); shape->SetBox(Vector3::ONE); } } // Create the camera. Limit far clip distance to match the fog // The camera needs to be created into a local node so that each client can retain its own camera, that is unaffected by // network messages. Furthermore, because the client removes all replicated scene nodes when connecting to a server scene, // the screen would become blank if the camera node was replicated (as only the locally created camera is assigned to a // viewport in SetupViewports() below) cameraNode_ = scene_->CreateChild("Camera", LOCAL); Camera* camera = cameraNode_->CreateComponent(); 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 SceneReplication::CreateUI() { ResourceCache* cache = GetSubsystem(); UI* ui = GetSubsystem(); UIElement* root = ui->GetRoot(); XMLFile* uiStyle = cache->GetResource("UI/DefaultStyle.xml"); // Set style to the UI root so that elements will inherit it root->SetDefaultStyle(uiStyle); // Create a Cursor UI element because we want to be able to hide and show it at will. When hidden, the mouse cursor will // control the camera, and when visible, it can interact with the login UI SharedPtr cursor(new Cursor(context_)); cursor->SetStyleAuto(uiStyle); ui->SetCursor(cursor); // Set starting position of the cursor at the rendering window center Graphics* graphics = GetSubsystem(); cursor->SetPosition(graphics->GetWidth() / 2, graphics->GetHeight() / 2); // Construct the instructions text element instructionsText_ = ui->GetRoot()->CreateChild(); instructionsText_->SetText( "Use WASD keys to move and RMB to rotate view" ); instructionsText_->SetFont(cache->GetResource("Fonts/Anonymous Pro.ttf"), 15); // Position the text relative to the screen center instructionsText_->SetHorizontalAlignment(HA_CENTER); instructionsText_->SetVerticalAlignment(VA_CENTER); instructionsText_->SetPosition(0, graphics->GetHeight() / 4); // Hide until connected instructionsText_->SetVisible(false); buttonContainer_ = root->CreateChild(); buttonContainer_->SetFixedSize(500, 20); buttonContainer_->SetPosition(20, 20); buttonContainer_->SetLayoutMode(LM_HORIZONTAL); textEdit_ = buttonContainer_->CreateChild(); textEdit_->SetStyleAuto(); connectButton_ = CreateButton("Connect", 90); disconnectButton_ = CreateButton("Disconnect", 100); startServerButton_ = CreateButton("Start Server", 110); UpdateButtons(); } void SceneReplication::SetupViewport() { Renderer* renderer = GetSubsystem(); // Set up a viewport to the Renderer subsystem so that the 3D scene can be seen SharedPtr viewport(new Viewport(context_, scene_, cameraNode_->GetComponent())); renderer->SetViewport(0, viewport); } void SceneReplication::SubscribeToEvents() { // Subscribe to fixed timestep physics updates for setting or applying controls SubscribeToEvent(E_PHYSICSPRESTEP, URHO3D_HANDLER(SceneReplication, HandlePhysicsPreStep)); // Subscribe HandlePostUpdate() method for processing update events. Subscribe to PostUpdate instead // of the usual Update so that physics simulation has already proceeded for the frame, and can // accurately follow the object with the camera SubscribeToEvent(E_POSTUPDATE, URHO3D_HANDLER(SceneReplication, HandlePostUpdate)); // Subscribe to button actions SubscribeToEvent(connectButton_, E_RELEASED, URHO3D_HANDLER(SceneReplication, HandleConnect)); SubscribeToEvent(disconnectButton_, E_RELEASED, URHO3D_HANDLER(SceneReplication, HandleDisconnect)); SubscribeToEvent(startServerButton_, E_RELEASED, URHO3D_HANDLER(SceneReplication, HandleStartServer)); // Subscribe to network events SubscribeToEvent(E_SERVERCONNECTED, URHO3D_HANDLER(SceneReplication, HandleConnectionStatus)); SubscribeToEvent(E_SERVERDISCONNECTED, URHO3D_HANDLER(SceneReplication, HandleConnectionStatus)); SubscribeToEvent(E_CONNECTFAILED, URHO3D_HANDLER(SceneReplication, HandleConnectionStatus)); SubscribeToEvent(E_CLIENTCONNECTED, URHO3D_HANDLER(SceneReplication, HandleClientConnected)); SubscribeToEvent(E_CLIENTDISCONNECTED, URHO3D_HANDLER(SceneReplication, HandleClientDisconnected)); // This is a custom event, sent from the server to the client. It tells the node ID of the object the client should control SubscribeToEvent(E_CLIENTOBJECTID, URHO3D_HANDLER(SceneReplication, HandleClientObjectID)); // Events sent between client & server (remote events) must be explicitly registered or else they are not allowed to be received GetSubsystem()->RegisterRemoteEvent(E_CLIENTOBJECTID); } Button* SceneReplication::CreateButton(const String& text, int width) { ResourceCache* cache = GetSubsystem(); Font* font = cache->GetResource("Fonts/Anonymous Pro.ttf"); Button* button = buttonContainer_->CreateChild