Browse Source

More documentation.
Tweaked NinjaSnowWar difficulty.
Moved CreateObject from Object to Context.
Made EventHandler & AnimationState RefCounted to simplify memory management.

Lasse Öörni 14 năm trước cách đây
mục cha
commit
9a5cbb6016

+ 2 - 2
Bin/Data/Scripts/AIController.as

@@ -1,8 +1,8 @@
 const float initialAggression = 0.0020;
 const float initialPrediction = 3000;
 const float initialAimSpeed = 5;
-const float deltaAggression = 0.00005;
-const float deltaPrediction = -20;
+const float deltaAggression = 0.000025;
+const float deltaPrediction = -15;
 const float deltaAimSpeed = 0.15;
 const float maxAggression = 0.01;
 const float maxPrediction = 2000;

+ 12 - 12
Docs/GettingStarted.dox

@@ -238,7 +238,7 @@ void SubscribeToEvents()
 
 The event handler function needs to have a specific signature. If event type and parameters are not needed, "void HandleEvent()", or "void HandleEvent(StringHash eventType, VariantMap& eventData)" if they are.
 
-We might want to expand the application later, so we use the latter form. The current frame's delta time is sent in the update event's parameters, and that will be useful when animating the scene. For now the event handler simply checks from the Input subsystem if the ESC key has been pressed; if it is, the Engine subsystem's Exit() function will be called. This closes the application window and causes Urho3D.exe to exit after the current main loop iteration finishes.
+We might want to expand the application later, so we use the latter form. The current frame's delta time is sent in the update event's parameters, and that will be useful when animating the scene. For now the event handler simply checks from the Input subsystem if the ESC key has been pressed; if it is, the Engine subsystem's \ref Engine::Exit() "Exit()" function will be called. This closes the application window and causes Urho3D.exe to exit after the current main loop iteration finishes.
 
 Note that we could also subscribe to the "KeyDown" event sent by the Input subsystem.
 
@@ -315,7 +315,7 @@ To start with, we need the include files for all the engine classes we are going
 #include <Windows.h>
 \endcode
 
-To be able to subscribe to events, we need to subclass Object (if we did not use events, we could do everything procedurally, for example directly in WinMain, but that would be somewhat ugly.) We name the class HelloWorld, with functions that match the script version, plus a constructor. Note the shared pointers to the scene that we will create, and to the ResourceCache, which is perhaps the most often used subsystem, and therefore convenient to store here. Also note the OBJECT macro, which inserts code for object type identification:
+To be able to subscribe to events, we need to subclass Object (if we did not use events, we could do everything procedurally, for example directly in WinMain, but that would be somewhat ugly.) We name the class HelloWorld, with functions that match the script version, plus a constructor. Note the shared pointers to the scene that we will create, and to the ResourceCache, which is perhaps the most often used subsystem, and therefore convenient to store here. Also note the OBJECT(className) macro, which inserts code for object type identification:
 
 \code
 class HelloWorld : public Object
@@ -337,7 +337,7 @@ public:
 
 Before the actual HelloWorld implementation, we define WinMain. First, we need to create the Context object, which holds all subsystems and object factories, and keeps track of event senders and receivers. All Object subclasses need to be supplied a pointer to that context. When using an object factory (such as when creating components) that is automatic, but when creating objects manually, the pointer also needs to be passed manually.
 
-With the context at hand, we create the Engine and initialize it. The arguments for the Initialize() function are the initial window title, the log file name, and command line parameters, which are parsed using the ParseArguments() helper function.
+With the context at hand, we create the Engine and initialize it. The arguments for the \ref Engine::Initialize() "Initialize()" function are the initial window title, the log file name, and command line parameters, which are parsed using the ParseArguments() helper function.
 
 After this, we instantiate the HelloWorld object, call its Start() function, and run the main loop until Engine tells that we should exit. The shared pointers will take care of deleting the objects in the correct order; the Context will be the last to be destroyed.
 
@@ -357,7 +357,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, in
 }
 \endcode
 
-Now we can start implementing HelloWorld. Note the OBJECTTYPESTATIC macro, which creates the static name and name hash for object type identification. For each OBJECT macro, a matching OBJECTTYPESTATIC must appear in a .cpp file. The reason to do that instead of defining them directly inside the OBJECT macro as function-static data is thread safety: if the first invocation to an object's GetTypeStatic() or GetTypeNameStatic() was started on several threads simultaneously, the results of function-static data initialization would be erratic.
+Now we can start implementing HelloWorld. Note the OBJECTTYPESTATIC(className) macro, which creates the static type name and type name hash for object type identification. For each OBJECT macro, a matching OBJECTTYPESTATIC must appear in a .cpp file.
 
 During construction, we store the ResourceCache subsystem pointer for later access:
 
@@ -377,20 +377,20 @@ In the Start() function the Scene will be created:
 void HelloWorld::Start()
 {
     helloScene_ = new Scene(context_);
-    
+
     CreateObjects();
     CreateText();
     SubscribeToEvents();
 }
 \endcode
 
-Like in the script example, CreateObjects() does the actual scene object creation and defines the viewport. Unlike in script, where properties were used to set the component values and scene node transforms, here we must use setter functions instead. Also, whereas strings were used in script to identify the components to create, here it is most convenient to use the template form of CreateComponent():
+Like in the script example, CreateObjects() does the actual scene object creation and defines the viewport. Unlike in script, where properties were used to set the component values and scene node transforms, here we must use setter functions instead. Also, whereas strings were used in script to identify the components to create, here it is most convenient to use the template form of \ref Node::CreateComponent() "CreateComponent()":
 
 \code
 void HelloWorld::CreateObjects()
 {
     helloScene_->CreateComponent<Octree>();
-    
+
     Node* objectNode = helloScene_->CreateChild();
     Node* lightNode = helloScene_->CreateChild();
     Node* cameraNode = helloScene_->CreateChild();
@@ -398,14 +398,14 @@ void HelloWorld::CreateObjects()
     StaticModel* object = objectNode->CreateComponent<StaticModel>();
     object->SetModel(cache_->GetResource<Model>("Models/Mushroom.mdl"));
     object->SetMaterial(cache_->GetResource<Material>("Materials/Mushroom.xml"));
-    
+
     Light* light = lightNode->CreateComponent<Light>();
     light->SetLightType(LIGHT_DIRECTIONAL);
     lightNode->SetDirection(Vector3(-1.0f, -1.0f, -1.0f));
-    
+
     Camera* camera = cameraNode->CreateComponent<Camera>();
     cameraNode->SetPosition(Vector3(0.0f, 0.3f, -3.0f));
-    
+
     GetSubsystem<Renderer>()->SetViewport(0, Viewport(helloScene_, camera));
 }
 \endcode
@@ -421,7 +421,7 @@ void HelloWorld::CreateText()
     helloText->SetColor(Color(0.0f, 1.0f, 0.0f));
     helloText->SetHorizontalAlignment(HA_CENTER);
     helloText->SetVerticalAlignment(VA_CENTER);
-    
+
     GetSubsystem<UI>()->GetRootElement()->AddChild(helloText);
 }
 \endcode
@@ -441,7 +441,7 @@ Unlike in script, in C++ the event handler function must always have the signatu
 void HelloWorld::HandleUpdate(StringHash eventType, VariantMap& eventData)
 {
     float timeStep = eventData[Update::P_TIMESTEP].GetFloat();
-    
+
     if (GetSubsystem<Input>()->GetKeyDown(KEY_ESC))
         GetSubsystem<Engine>()->Exit();
 }

+ 125 - 4
Docs/Reference.dox

@@ -1,18 +1,139 @@
 /**
 
-\page ObjectTypes Object types and factories
+\page ObjectTypes %Object types and factories
+
+Classes that derive from Object contain type-identification, they can be created through object factories, and they can send and receive \ref Events "events". Examples of these are all Component and UIElement subclasses. To be able to be constructed by a factory, they need to have a constructor that takes a Context pointer as the only parameter.
+
+%Object factory registration and object creation through factories are directly accessible only in C++, not in script.
+
+The definition of an Object subclass must contain the OBJECT(className) macro. Type identification is available both as text (GetTypeName() or GetTypeNameStatic()) and as a 16-bit hash of the type name (GetType() or GetTypeStatic()).
+
+In addition the OBJECTTYPESTATIC(className) macro must appear in a .cpp file to actually define the type identification data. The reason for doing this instead of defining the data directly inside the OBJECT macro as function-static data is thread safety: if the first invocation to an object's GetTypeStatic() or GetTypeNameStatic() was started on several threads simultaneously, the results of function-static data initialization would be erratic.
+
+To register an object factory for a specific type, call the \ref Context::RegisterFactory() "RegisterFactory()" template function on Context. You can get its pointer from any Object either via the \ref Object::context_ "context_" member variable, or by calling \ref Object::GetContext() "GetContext()". An example:
+
+\code
+context_->RegisterFactory<Camera>();
+\endcode
+
+To create an object using a factory, call Context's \ref Context::CreateObject() "CreateObject()" function. This takes the 16-bit hash of the type name as a parameter. The created object (or null if there was no matching factory registered) will be returned inside a SharedPtr<Object>. For example:
+
+\code
+SharedPtr<Object> newComponent = context_->CreateObject(type));
+\endcode
 
 
 \page Subsystems Subsystems
 
+Any Object can be registered to the Context as a subsystem, by using the function \ref Context::RegisterSubsystem() "RegisterSubsystem()". They can then be accessed by any other Object inside the same context by calling \ref Object::GetSubsystem() "GetSubsystem()". Only one instance of each object type can exist as a subsystem.
+
+After Engine initialization, the following subsystems will always exist:
+
+- Time: manages frame updates, frame number and elapsed time counting, and controls the frequency of the operating system low-resolution timer.
+- FileSystem: provides directory operations.
+- Log: provides logging services.
+- ResourceCache: loads resources and keeps them cached for later access.
+- Network: provides UDP networking.
+- Input: handles keyboard and mouse input. Will be inactive in headless mode.
+- UI: the graphical user interface. Will be inactive in headless mode.
+- Audio: provides sound output. Will be inactive if sound disabled.
+- Engine: creates the other subsystems and controls the main loop iteration and framerate limiting.
+
+The following subsystems are optional, so GetSubsystem() may return null if they have not been created:
+
+- Profiler: Provides hierarchical function execution time measurement using the operating system performance counter. Exists if profiling has been compiled in (configurable from the root CMakeLists.txt)
+- Graphics: Manages the application window and the Direct3D9 device and resources. Exists if not in headless mode.
+- Renderer: Renders scenes in 3D and manages rendering quality settings. Exists if not in headless mode.
+- Script: Provides the AngelScript execution environment. Created by calling \ref Engine::InitializeScripting() "InitializeScripting()".
+- Console: provides an interactive AngelScript console and log display. Created by calling \ref Engine::CreateConsole() "CreateConsole()".
+- DebugHud: displays rendering mode information and statistics and profiling data. Created by calling \ref Engine::CreateDebugHud() "CreateDebugHud()".
+
+In script, the subsystems are available through the following global properties:
+time, fileSystem, log, cache, network, input, ui, audio, engine, graphics, renderer, script, console, debugHud. Note that Profiler is not available to script due to its low-level nature.
+
 
 \page Events Events
 
+The Urho3D event system allows for data transport and function invocation without the sender and receiver having to explicitly know of each other. It supports both broadcast and targeted events. Both the event sender and receiver must derive from Object. An event receiver must subscribe to each event type it wishes to receive: one can either subscribe to the event coming from any sender, or from a specific sender. The latter is useful for example when handling events from the user interface elements.
+
+Events themselves do not need to be registered. They are identified by 32-bit hashes of their names. Event parameters (the data payload) are optional and are contained inside a VariantMap, identified by 16-bit parameter name hashes. For the inbuilt Urho3D events, event type (E_UPDATE, E_KEYDOWN, E_MOUSEMOVE etc.) and parameter hashes (P_TIMESTEP, P_DX, P_DY etc.) are defined as constants inside include files such as CoreEvents.h or InputEvents.h.
+
+When subscribing to an event, a handler function must be specified. In C++ these must have the signature void HandleEvent(StringHash eventType, VariantMap& eventData). The HANDLER(className, function) macro helps in defining the required class-specific function pointers. For example:
+
+\code
+SubscribeToEvent(E_UPDATE, HANDLER(MyClass, MyEventHandler));
+\endcode
+
+In script events are identified by their string names instead of name hashes (though these are internally converted to hashes.) Script event handlers can either have the same signature as in C++, or a simplified signature void HandleEvent() when event type and parameters are not required. The same event subscription would look like:
+
+\code
+SubscribeToEvent("Update", "MyEventHandler");
+\endcode
+
+In C++ events must always be handled by a member function. In script procedural event handling is also possible; in this case the ScriptFile where the event handler function is located becomes the event receiver. See \ref Scripting "Scripting" for more details.
+
+To send an event, fill the event parameters (if necessary) and call \ref Object::SendEvent "SendEvent()". For example, this (in C++) is how the Time subsystem sends the Update event. Note how for the inbuilt Urho3D events, the parameter name hashes are always put inside a namespace (the event's name) to prevent name clashes:
+
+\code
+using namespace Update;
+
+VariantMap eventData;
+eventData[P_TIMESTEP] = timeStep_;
+SendEvent(E_UPDATE, eventData);
+\endcode
+
+In script event parameters, like event types, are referred to with strings, so the same code would look like:
+
+\code
+VariantMap eventData;
+eventData["TimeStep"] = timeStep;
+SendEvent("Update", eventData);
+\endcode
+
+Events can also be unsubscribed from. See \ref Object::UnsubscribeFromEvent "UnsubscribeFromEvent()" for details.
+
 
 \page MainLoop Main loop and frame update
 
+The main loop iteration (also called a frame) is driven by the Engine. In contrast it is the program's (for example Urho3D.exe) responsibility to continuously loop this iteration. The iteration consists of the Engine calling the Time subsystem's \ref Time::BeginFrame "BeginFrame()" and \ref Time::EndFrame "EndFrame()" functions, which causes several events to be sent:
+
+- E_BEGINFRAME: signals the beginning of the new frame. Input and Network react to this to check for operating system window messages and arrived network packets.
+- E_UPDATE: application-wide logic update event. By default each active Scene reacts to this and triggers the scene update (more on this below.)
+- E_POSTUPDATE: application-wide logic post-update event. The UI subsystem updates its logic here.
+- E_RENDERUPDATE: Renderer updates its viewports here to prepare for rendering, and the UI generates render commands necessary to render the user interface.
+- E_POSTRENDERUPDATE: by default nothing hooks to this. This can be used to implement logic that requires the rendering views to be up-to-date (for example to do accurate raycasts.) Scenes may not be modified at this point (especially scene objects may not be deleted or crashes may occur.)
+- E_ENDFRAME: signals the end of the frame. Before this, rendering the frame and measuring the next frame's timestep will have occurred.
+
+The update of each Scene causes further events to be sent:
+
+- E_SCENEUPDATE: variable timestep scene update. This is a good place to implement any scene logic that does not need to happen at a fixed step.
+- E_SCENESUBSYSTEMUPDATE: update scene-wide subsystems. Currently only the PhysicsWorld component listens to this, which causes it to step the physics simulation and send the following two events for each simulation step:
+- E_PHYSICSPRESTEP: called before the simulation iteration. Happens at a fixed rate (the physics FPS.) If fixed timestep logic updates are needed, this is a good event to listen to.
+- E_PHYSICSPOSTSTEP: called after the simulation iteration. Happens at the same rate as E_PHYSICSPRESTEP.
+- E_SCENEPOSTUPDATE: variable timestep scene post-update. ParticleEmitter and AnimationController update themselves as a response to this event.
+
+Variable timestep logic updates are preferable to fixed timestep, because they are only executed once per frame. In contrast, if the rendering framerate is low, several physics world simulation steps will be performed on each frame to keep up the apparent passage if time, and if this also causes a lot of logic code to be executed for each step, the program may bog down further if the CPU can not handle the load.
+
+
+\page SceneModel %Scene model
+
+Urho3D's scene model can be described as a component-based scene graph. The Scene consists of a hierarchy of scene nodes, starting from the root node, which also represents the whole scene. Each Node has a 3D transform (position, rotation and scale) and a name, but no other functionality. Rendering 3D objects, sound playback, physics and scripted logic updates are all enabled by creating \ref Component "Components" into the nodes.
+
+For more information on the component-based scene model, see for example http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/.
+
+Components created into the Scene itself have a special role: to implement scene-wide functionality. They should be created before all other components, and include the following:
+
+- Octree: implements visibility queries. Without this 3D objects can not be rendered.
+- PhysicsWorld: implements physics simulation. Physics components such as RigidBody or CollisionShape can not function properly without this.
+- DebugRenderer: implements debug geometry rendering.
+
+"Ordinary" components like Light, Camera or StaticModel should not be created directly into the Scene, but rather into child nodes.
+
+A Scene can be either active or inactive (paused.) Active scenes will be automatically updated on each main loop iteration. Scenes can be loaded and saved in either binary or XML format; see \ref Serialization "Serialization" for details.
+
+%Scene nodes can be freely reparented. In contrast components are always created to the node they belong to, and can not be moved between nodes. Both child nodes and components are stored using SharedPtr containers; this means that detaching a child node from its parent or removing a component will also destroy it, if no other reference to it exists. Both Node & Component provide the \ref Node::Remove() "Remove()" function to accomplish this without having to go through the parent. Note that no operations on the node or component in question are safe after calling it.
 
-\page SceneModel Scene model
+When created, nodes and components get unique integer IDs starting from 1. They can be queried from the Scene by using the functions \ref Scene::GetNodeByID "GetNodeByID()" and \ref Scene::GetComponentByID "GetComponentByID()". This is much faster than for example doing recursive name-based scene node queries.
 
 
 \page Resources Resources
@@ -24,10 +145,10 @@
 \page Rendering Rendering
 
 
-\page Input Input
+\page Input %Input
 
 
-\page Audio Audio
+\page Audio %Audio
 
 
 \page Physics Physics

+ 9 - 0
Engine/Core/Context.cpp

@@ -45,6 +45,15 @@ Context::~Context()
     factories_.clear();
 }
 
+SharedPtr<Object> Context::CreateObject(ShortStringHash objectType)
+{
+    std::map<ShortStringHash, SharedPtr<ObjectFactory> >::const_iterator i = factories_.find(objectType);
+    if (i != factories_.end())
+        return i->second->CreateObject();
+    else
+        return SharedPtr<Object>();
+}
+
 void Context::RegisterFactory(ObjectFactory* factory)
 {
     if (!factory)

+ 4 - 2
Engine/Core/Context.h

@@ -39,6 +39,8 @@ public:
     /// Destruct
     ~Context();
     
+    /// Create an object by type. Return pointer to it or null if no factory found
+    SharedPtr<Object> CreateObject(ShortStringHash objectType);
     /// Register a factory for an object type. If exists already, will not be replaced
     void RegisterFactory(ObjectFactory* factory);
     /// Register a subsystem. If exists already, will not be replaced
@@ -138,7 +140,7 @@ public:
     EventHandler* GetHandler() const { return handler_; }
     /// Return object type name from hash, or null if unknown
     const char* GetTypeName(ShortStringHash type) const;
-    /// Template version of returning subsystem
+    /// Template version of returning a subsystem
     template <class T> T* GetSubsystem() const;
     
     /// Return attribute descriptions for an object type, or null if none defined
@@ -181,7 +183,7 @@ private:
     /// Event types for specific senders that have had receivers removed during event handling
     std::set<std::pair<Object*, StringHash> > dirtySpecificReceivers_;
     /// Active event handler. Not stored in a stack for performance reasons; is needed only in esoteric cases
-    EventHandler* handler_;
+    WeakPtr<EventHandler> handler_;
 };
 
 template <class T> void Context::RegisterFactory()

+ 13 - 41
Engine/Core/Object.cpp

@@ -48,7 +48,7 @@ void Object::OnEvent(Object* sender, bool broadcast, StringHash eventType, Varia
     WeakPtr<Object> self(this);
     
     // Check first the specific event handlers, which have priority
-    std::map<std::pair<Object*, StringHash>, EventHandler*>::const_iterator i = eventHandlers_.find(
+    std::map<std::pair<Object*, StringHash>, SharedPtr<EventHandler> >::const_iterator i = eventHandlers_.find(
         std::make_pair(sender, eventType));
     if (i != eventHandlers_.end())
     {
@@ -70,16 +70,6 @@ void Object::OnEvent(Object* sender, bool broadcast, StringHash eventType, Varia
     }
 }
 
-SharedPtr<Object> Object::CreateObject(ShortStringHash type)
-{
-    const std::map<ShortStringHash, SharedPtr<ObjectFactory> >& factories = context_->GetObjectFactories();
-    std::map<ShortStringHash, SharedPtr<ObjectFactory> >::const_iterator i = factories.find(type);
-    if (i != factories.end())
-        return i->second->CreateObject();
-    else
-        return SharedPtr<Object>();
-}
-
 void Object::SubscribeToEvent(StringHash eventType, EventHandler* handler)
 {
     if (!handler)
@@ -87,11 +77,6 @@ void Object::SubscribeToEvent(StringHash eventType, EventHandler* handler)
     
     std::pair<Object*, StringHash> combination((Object*)0, eventType);
     
-    // If there already is a handler, delete it
-    std::map<std::pair<Object*, StringHash>, EventHandler*>::iterator i = eventHandlers_.find(combination);
-    if (i != eventHandlers_.end())
-        delete i->second;
-    
     eventHandlers_[combination] = handler;
     context_->AddEventReceiver(this, eventType);
 }
@@ -103,28 +88,22 @@ void Object::SubscribeToEvent(Object* sender, StringHash eventType, EventHandler
     
     std::pair<Object*, StringHash> combination(sender, eventType);
     
-    // If there already is a handler, delete it
-    std::map<std::pair<Object*, StringHash>, EventHandler*>::iterator i = eventHandlers_.find(combination);
-    if (i != eventHandlers_.end())
-        delete i->second;
-    
     eventHandlers_[combination] = handler;
     context_->AddEventReceiver(this, sender, eventType);
 }
 
 void Object::UnsubscribeFromEvent(StringHash eventType)
 {
-    for (std::map<std::pair<Object*, StringHash>, EventHandler*>::iterator i = eventHandlers_.begin();
+    for (std::map<std::pair<Object*, StringHash>, SharedPtr<EventHandler> >::iterator i = eventHandlers_.begin();
         i != eventHandlers_.end();)
     {
-        std::map<std::pair<Object*, StringHash>, EventHandler*>::iterator current = i++;
+        std::map<std::pair<Object*, StringHash>, SharedPtr<EventHandler> >::iterator current = i++;
         if (current->first.second == eventType)
         {
             if (current->first.first)
                 context_->RemoveEventReceiver(this, current->first.first, current->first.second);
             else
                 context_->RemoveEventReceiver(this, current->first.second);
-            delete current->second;
             eventHandlers_.erase(current);
         }
     }
@@ -134,11 +113,10 @@ void Object::UnsubscribeFromEvent(Object* sender, StringHash eventType)
 {
     std::pair<Object*, StringHash> combination(sender, eventType);
     
-    std::map<std::pair<Object*, StringHash>, EventHandler*>::iterator i = eventHandlers_.find(combination);
+    std::map<std::pair<Object*, StringHash>, SharedPtr<EventHandler> >::iterator i = eventHandlers_.find(combination);
     if (i != eventHandlers_.end())
     {
         context_->RemoveEventReceiver(this, i->first.first, i->first.second);
-        delete i->second;
         eventHandlers_.erase(i);
     }
 }
@@ -148,14 +126,13 @@ void Object::UnsubscribeFromEvents(Object* sender)
     if (!sender)
         return;
     
-    for (std::map<std::pair<Object*, StringHash>, EventHandler*>::iterator i = eventHandlers_.begin();
+    for (std::map<std::pair<Object*, StringHash>, SharedPtr<EventHandler> >::iterator i = eventHandlers_.begin();
         i != eventHandlers_.end();)
     {
-        std::map<std::pair<Object*, StringHash>, EventHandler*>::iterator current = i++;
+        std::map<std::pair<Object*, StringHash>, SharedPtr<EventHandler> >::iterator current = i++;
         if (current->first.first == sender)
         {
             context_->RemoveEventReceiver(this, current->first.first, current->first.second);
-            delete current->second;
             eventHandlers_.erase(current);
         }
     }
@@ -163,14 +140,13 @@ void Object::UnsubscribeFromEvents(Object* sender)
 
 void Object::UnsubscribeFromAllEvents()
 {
-    for (std::map<std::pair<Object*, StringHash>, EventHandler*>::iterator i = eventHandlers_.begin();
+    for (std::map<std::pair<Object*, StringHash>, SharedPtr<EventHandler> >::iterator i = eventHandlers_.begin();
         i != eventHandlers_.end(); ++i)
     {
         if (i->first.first)
             context_->RemoveEventReceiver(this, i->first.first, i->first.second);
         else
             context_->RemoveEventReceiver(this, i->first.second);
-        delete i->second;
     }
     
     eventHandlers_.clear();
@@ -178,17 +154,16 @@ void Object::UnsubscribeFromAllEvents()
 
 void Object::UnsubscribeFromAllEventsWithUserData()
 {
-    for (std::map<std::pair<Object*, StringHash>, EventHandler*>::iterator i = eventHandlers_.begin();
+    for (std::map<std::pair<Object*, StringHash>, SharedPtr<EventHandler> >::iterator i = eventHandlers_.begin();
         i != eventHandlers_.end(); )
     {
         if (i->second->GetUserData())
         {
-            std::map<std::pair<Object*, StringHash>, EventHandler*>::iterator current = i++;
+            std::map<std::pair<Object*, StringHash>, SharedPtr<EventHandler> >::iterator current = i++;
             if (current->first.first)
                 context_->RemoveEventReceiver(this, current->first.first, current->first.second);
             else
                 context_->RemoveEventReceiver(this, current->first.second);
-            delete current->second;
             eventHandlers_.erase(current);
         }
         else
@@ -304,12 +279,12 @@ Object* Object::GetSubsystem(ShortStringHash type) const
         return 0;
 }
 
-bool Object::HasSubscribed(StringHash eventType) const
+bool Object::HasSubscribedToEvent(StringHash eventType) const
 {
     return eventHandlers_.find(std::make_pair((Object*)0, eventType)) != eventHandlers_.end();
 }
 
-bool Object::HasSubscribed(Object* sender, StringHash eventType) const
+bool Object::HasSubscribedToEvent(Object* sender, StringHash eventType) const
 {
     return eventHandlers_.find(std::make_pair(sender, eventType)) != eventHandlers_.end();
 }
@@ -321,14 +296,11 @@ Object* Object::GetSender() const
 
 void Object::RemoveEventSender(Object* sender)
 {
-    for (std::map<std::pair<Object*, StringHash>, EventHandler*>::iterator i = eventHandlers_.begin();
+    for (std::map<std::pair<Object*, StringHash>, SharedPtr<EventHandler> >::iterator i = eventHandlers_.begin();
         i != eventHandlers_.end();)
     {
-        std::map<std::pair<Object*, StringHash>, EventHandler*>::iterator current = i++;
+        std::map<std::pair<Object*, StringHash>, SharedPtr<EventHandler> >::iterator current = i++;
         if (current->first.first == sender)
-        {
-            delete current->second;
             eventHandlers_.erase(current);
-        }
     }
 }

+ 5 - 7
Engine/Core/Object.h

@@ -45,8 +45,6 @@ public:
     /// Handle event
     virtual void OnEvent(Object* sender, bool broadcast, StringHash eventType, VariantMap& eventData);
     
-    /// Create object by type
-    SharedPtr<Object> CreateObject(ShortStringHash type);
     /// Subscribe to an event that can be sent by any sender
     void SubscribeToEvent(StringHash eventType, EventHandler* handler);
     /// Subscribe to a specific sender's event
@@ -59,7 +57,7 @@ public:
     void UnsubscribeFromEvents(Object* sender);
     /// Unsubscribe from all events
     void UnsubscribeFromAllEvents();
-    /// Unsubscribe from all events with userdata defined in the handler (possibly scripted)
+    /// Unsubscribe from all events with userdata defined in the handler
     void UnsubscribeFromAllEventsWithUserData();
     /// Send event to all subscribers
     void SendEvent(StringHash eventType);
@@ -79,9 +77,9 @@ public:
     /// Return subsystem by type
     Object* GetSubsystem(ShortStringHash type) const;
     /// Return whether has subscribed to an event without specific sender
-    bool HasSubscribed(StringHash eventType) const;
+    bool HasSubscribedToEvent(StringHash eventType) const;
     /// Return whether has subscribed to a specific sender's event
-    bool HasSubscribed(Object* sender, StringHash eventType) const;
+    bool HasSubscribedToEvent(Object* sender, StringHash eventType) const;
     /// Return active event sender
     Object* GetSender() const;
     /// Template version of returning a subsystem
@@ -96,7 +94,7 @@ protected:
     
 private:
     /// Event handlers. Sender is null for non-specific handlers
-    std::map<std::pair<Object*, StringHash>, EventHandler*> eventHandlers_;
+    std::map<std::pair<Object*, StringHash>, SharedPtr<EventHandler> > eventHandlers_;
 };
 
 template <class T> SharedPtr<T> Object::CreateObject()
@@ -160,7 +158,7 @@ public:
 };
 
 /// Internal helper class for invoking event handler functions
-class EventHandler
+class EventHandler : public RefCounted
 {
 public:
     /// Construct with specified receiver

+ 8 - 0
Engine/Engine/APITemplates.h

@@ -237,6 +237,14 @@ template <class T> void RegisterDeserializer(asIScriptEngine* engine, const char
     engine->RegisterObjectMethod(className, "bool get_eof() const", asMETHODPR(T, IsEof, () const, bool), asCALL_THISCALL);
 }
 
+/// Template function for registering a class derived from RefCounted
+template <class T> void RegisterRefCounted(asIScriptEngine* engine, const char* className)
+{
+    engine->RegisterObjectType(className, 0, asOBJ_REF);
+    engine->RegisterObjectBehaviour(className, asBEHAVE_ADDREF, "void f()", asMETHODPR(T, AddRef, (), void), asCALL_THISCALL);
+    engine->RegisterObjectBehaviour(className, asBEHAVE_RELEASE, "void f()", asMETHODPR(T, ReleaseRef, (), void), asCALL_THISCALL);
+}
+
 /// Template function for registering a class derived from Object
 template <class T> void RegisterObject(asIScriptEngine* engine, const char* className)
 {

+ 1 - 3
Engine/Engine/GraphicsAPI.cpp

@@ -562,9 +562,7 @@ static void AnimatedModelSetModel(Model* model, AnimatedModel* ptr)
 
 static void RegisterAnimatedModel(asIScriptEngine* engine)
 {
-    engine->RegisterObjectType("AnimationState", 0, asOBJ_REF);
-    engine->RegisterObjectBehaviour("AnimationState", asBEHAVE_ADDREF, "void f()", asFUNCTION(FakeAddRef), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectBehaviour("AnimationState", asBEHAVE_RELEASE, "void f()", asFUNCTION(FakeReleaseRef), asCALL_CDECL_OBJLAST);
+    RegisterRefCounted<AnimationState>(engine, "AnimationState");
     engine->RegisterObjectMethod("AnimationState", "void AddWeight(float)", asMETHOD(AnimationState, AddWeight), asCALL_THISCALL);
     engine->RegisterObjectMethod("AnimationState", "void AddTime(float)", asMETHOD(AnimationState, AddTime), asCALL_THISCALL);
     engine->RegisterObjectMethod("AnimationState", "void set_startBone(Bone@+)", asMETHOD(AnimationState, SetStartBone), asCALL_THISCALL);

+ 11 - 17
Engine/Graphics/AnimatedModel.cpp

@@ -50,7 +50,7 @@
 
 static const Vector3 dotScale(1 / 3.0f, 1 / 3.0f, 1 / 3.0f);
 
-static bool CompareAnimationOrder(AnimationState* lhs, AnimationState* rhs)
+static bool CompareAnimationOrder(SharedPtr<AnimationState> lhs, SharedPtr<AnimationState> rhs)
 {
     return lhs->GetLayer() < rhs->GetLayer();
 }
@@ -74,7 +74,6 @@ AnimatedModel::AnimatedModel(Context* context) :
 
 AnimatedModel::~AnimatedModel()
 {
-    RemoveAllAnimationStates();
 }
 
 void AnimatedModel::RegisterObject(Context* context)
@@ -174,7 +173,7 @@ Variant AnimatedModel::OnGetAttribute(const AttributeInfo& attr)
         {
             VectorBuffer buf;
             buf.WriteVLE(animationStates_.size());
-            for (std::vector<AnimationState*>::iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
+            for (std::vector<SharedPtr<AnimationState> >::iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
             {
                 AnimationState* state = *i;
                 Bone* startBone = state->GetStartBone();
@@ -430,7 +429,7 @@ AnimationState* AnimatedModel::AddAnimationState(Animation* animation)
     if (existing)
         return existing;
     
-    AnimationState* newState = new AnimationState(this, animation);
+    SharedPtr<AnimationState> newState(new AnimationState(this, animation));
     animationStates_.push_back(newState);
     MarkAnimationOrderDirty();
     return newState;
@@ -449,14 +448,13 @@ void AnimatedModel::RemoveAnimationState(const std::string& animationName)
 
 void AnimatedModel::RemoveAnimationState(StringHash animationNameHash)
 {
-    for (std::vector<AnimationState*>::iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
+    for (std::vector<SharedPtr<AnimationState> >::iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
     {
         AnimationState* state = *i;
         Animation* animation = state->GetAnimation();
         // Check both the animation and the resource name
         if ((animation->GetNameHash() == animationNameHash) || (animation->GetAnimationNameHash() == animationNameHash))
         {
-            delete state;
             animationStates_.erase(i);
             MarkAnimationDirty();
         }
@@ -465,11 +463,10 @@ void AnimatedModel::RemoveAnimationState(StringHash animationNameHash)
 
 void AnimatedModel::RemoveAnimationState(AnimationState* state)
 {
-    for (std::vector<AnimationState*>::iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
+    for (std::vector<SharedPtr<AnimationState> >::iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
     {
         if (*i == state)
         {
-            delete state;
             animationStates_.erase(i);
             MarkAnimationDirty();
             return;
@@ -479,9 +476,6 @@ void AnimatedModel::RemoveAnimationState(AnimationState* state)
 
 void AnimatedModel::RemoveAllAnimationStates()
 {
-    for (std::vector<AnimationState*>::iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
-        delete *i;
-    
     animationStates_.clear();
     MarkAnimationDirty();
 }
@@ -597,7 +591,7 @@ float AnimatedModel::GetMorphWeight(StringHash nameHash) const
 
 AnimationState* AnimatedModel::GetAnimationState(Animation* animation) const
 {
-    for (std::vector<AnimationState*>::const_iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
+    for (std::vector<SharedPtr<AnimationState> >::const_iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
     {
         if ((*i)->GetAnimation() == animation)
             return *i;
@@ -608,7 +602,7 @@ AnimationState* AnimatedModel::GetAnimationState(Animation* animation) const
 
 AnimationState* AnimatedModel::GetAnimationState(const std::string& animationName) const
 {
-    for (std::vector<AnimationState*>::const_iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
+    for (std::vector<SharedPtr<AnimationState> >::const_iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
     {
         Animation* animation = (*i)->GetAnimation();
         
@@ -622,7 +616,7 @@ AnimationState* AnimatedModel::GetAnimationState(const std::string& animationNam
 
 AnimationState* AnimatedModel::GetAnimationState(StringHash animationNameHash) const
 {
-    for (std::vector<AnimationState*>::const_iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
+    for (std::vector<SharedPtr<AnimationState> >::const_iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
     {
         Animation* animation = (*i)->GetAnimation();
         
@@ -636,7 +630,7 @@ AnimationState* AnimatedModel::GetAnimationState(StringHash animationNameHash) c
 
 AnimationState* AnimatedModel::GetAnimationState(unsigned index) const
 {
-    return index < animationStates_.size() ? animationStates_[index] : 0;
+    return index < animationStates_.size() ? animationStates_[index].GetPtr() : 0;
 }
 
 void AnimatedModel::SetSkeleton(const Skeleton& skeleton, bool createBones)
@@ -765,7 +759,7 @@ void AnimatedModel::AssignBoneNodes()
     }
     
     // Re-assign the same start bone to get the proper bone node this time
-    for (std::vector<AnimationState*>::iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
+    for (std::vector<SharedPtr<AnimationState> >::iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
     {
         AnimationState* state = *i;
         state->SetStartBone(state->GetStartBone());
@@ -917,7 +911,7 @@ void AnimatedModel::UpdateAnimation(const FrameInfo& frame)
     
     // Reset skeleton, then apply all animations
     skeleton_.Reset();
-    for (std::vector<AnimationState*>::iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
+    for (std::vector<SharedPtr<AnimationState> >::iterator i = animationStates_.begin(); i != animationStates_.end(); ++i)
         (*i)->Apply();
     
     // Animation has changed the bounding box: mark node for octree reinsertion

+ 2 - 2
Engine/Graphics/AnimatedModel.h

@@ -97,7 +97,7 @@ public:
     /// Return skeleton
     Skeleton& GetSkeleton() { return skeleton_; }
     /// Return all animation states
-    const std::vector<AnimationState*>& GetAnimationStates() const { return animationStates_; }
+    const std::vector<SharedPtr<AnimationState> >& GetAnimationStates() const { return animationStates_; }
     /// Return number of animation states
     unsigned GetNumAnimationStates() const { return animationStates_.size(); }
     /// Return animation state by animation pointer
@@ -168,7 +168,7 @@ private:
     /// Vertex morphs
     std::vector<ModelMorph> morphs_;
     /// Animation states
-    std::vector<AnimationState*> animationStates_;
+    std::vector<SharedPtr<AnimationState> > animationStates_;
     /// Skinning matrices
     std::vector<Matrix4x3> skinMatrices_;
     /// Mapping of subgeometry bone indices, used if more bones than skinning shader can manage

+ 12 - 1
Engine/Graphics/AnimationState.cpp

@@ -33,6 +33,7 @@
 
 AnimationState::AnimationState(AnimatedModel* model, Animation* animation) :
     model_(model),
+    animation_(animation),
     startBone_(0),
     looped_(false),
     weight_(0.0f),
@@ -40,7 +41,6 @@ AnimationState::AnimationState(AnimatedModel* model, Animation* animation) :
     layer_(0),
     useNlerp_(false)
 {
-    animation_ = animation;
     SetStartBone(0);
     
     // Setup a cache for last keyframe of each track
@@ -55,6 +55,12 @@ AnimationState::~AnimationState()
 
 void AnimationState::SetStartBone(Bone* startBone)
 {
+    if (!model_)
+    {
+        startBone_ = 0;
+        return;
+    }
+    
     Skeleton& skeleton = model_->GetSkeleton();
     const std::vector<Bone>& bones = skeleton.GetBones();
     Bone* rootBone = skeleton.GetRootBone();
@@ -157,6 +163,11 @@ void AnimationState::SetUseNlerp(bool enable)
     useNlerp_ = enable;
 }
 
+Bone* AnimationState::GetStartBone() const
+{
+    return model_ ? startBone_ : 0;
+}
+
 float AnimationState::GetLength() const
 {
     return animation_->GetLength();

+ 5 - 5
Engine/Graphics/AnimationState.h

@@ -36,7 +36,7 @@ struct AnimationTrack;
 struct Bone;
 
 /// Animation instance in an animated model
-class AnimationState
+class AnimationState : public RefCounted
 {
 public:
     /// Construct with animated model and animation pointers
@@ -64,7 +64,7 @@ public:
     /// Return animation
     Animation* GetAnimation() const { return animation_; }
     /// Return start bone
-    Bone* GetStartBone() const { return startBone_; }
+    Bone* GetStartBone() const;
     /// Return whether weight is nonzero
     bool IsEnabled() const { return weight_ > 0.0f; }
     /// Return whether looped
@@ -85,11 +85,11 @@ public:
     
 private:
     /// Animated model
-    AnimatedModel* model_;
-    /// Start bone
-    Bone* startBone_;
+    WeakPtr<AnimatedModel> model_;
     /// Animation
     SharedPtr<Animation> animation_;
+    /// Start bone
+    Bone* startBone_;
     /// Mapping of animation track indices to bones
     std::map<unsigned, Bone*> trackToBoneMap_;
     /// Last keyframe on each animation track for optimized keyframe search

+ 1 - 1
Engine/Resource/ResourceCache.cpp

@@ -366,7 +366,7 @@ Resource* ResourceCache::GetResource(ShortStringHash type, StringHash nameHash)
     }
     
     // Make sure the pointer is non-null and is a Resource subclass
-    resource = DynamicCast<Resource>(CreateObject(type));
+    resource = DynamicCast<Resource>(context_->CreateObject(type));
     if (!resource)
     {
         LOGERROR("Could not load unknown resource type " + ToString(type));

+ 1 - 1
Engine/Scene/Node.cpp

@@ -664,7 +664,7 @@ Component* Node::GetComponent(ShortStringHash type, unsigned index) const
 Component* Node::CreateComponent(ShortStringHash type, unsigned id)
 {
     // Make sure the object in question is a component
-    SharedPtr<Component> newComponent = DynamicCast<Component>(CreateObject(type));
+    SharedPtr<Component> newComponent = DynamicCast<Component>(context_->CreateObject(type));
     if (!newComponent)
     {
         LOGERROR("Could not create unknown component type " + ToString(type));

+ 1 - 1
Engine/Script/ScriptInstance.cpp

@@ -243,7 +243,7 @@ void ScriptInstance::DelayedExecute(float delay, const std::string& declaration,
     delayedMethodCalls_.push_back(call);
     
     // Make sure we are registered to the scene update event, because delayed calls are executed there
-    if ((!methods_[METHOD_UPDATE]) && (!HasSubscribed(E_SCENEUPDATE)))
+    if ((!methods_[METHOD_UPDATE]) && (!HasSubscribedToEvent(E_SCENEUPDATE)))
     {
         Node* node = GetNode();
         if (node)

+ 2 - 2
Engine/UI/UI.cpp

@@ -304,7 +304,7 @@ SharedPtr<UIElement> UI::LoadLayout(XMLFile* file, XMLFile* styleFile)
     }
     
     std::string type = rootElem.GetString("type");
-    root = StaticCast<UIElement>(CreateObject(ShortStringHash(type)));
+    root = StaticCast<UIElement>(context_->CreateObject(ShortStringHash(type)));
     if (!root)
     {
         LOGERROR("Could not create UI element " + type);
@@ -508,7 +508,7 @@ void UI::LoadLayout(UIElement* current, const XMLElement& elem, XMLFile* styleFi
     {
         // Create element
         std::string type = childElem.GetString("type");
-        SharedPtr<UIElement> child = StaticCast<UIElement>(CreateObject(ShortStringHash(type)));
+        SharedPtr<UIElement> child = StaticCast<UIElement>(context_->CreateObject(ShortStringHash(type)));
         if (!child)
         {
             LOGERROR("Could not create UI element " + type);

+ 1 - 1
Engine/UI/UI.h

@@ -53,7 +53,7 @@ public:
     void Clear();
     /// Update the UI logic. Called by HandlePostUpdate()
     void Update(float timeStep);
-    /// Update the UI for rendering. Called by HandlePostRenderUpdate()
+    /// Update the UI for rendering. Called by HandleRenderUpdate()
     void RenderUpdate();
     /// Render the UI
     void Render();