Parcourir la source

Added snowballs to the scripted NinjaSnowWar.
Documentation update.

Lasse Öörni il y a 15 ans
Parent
commit
d4666eaacb

+ 73 - 2
Bin/Data/Scripts/GameObject.as

@@ -4,15 +4,15 @@ class GameObject : ScriptObject
 {
     Controls controls;
     Controls prevControls;
-    float duration;
     bool onGround;
     bool isSliding;
+    float duration;
 
     GameObject()
     {
-        duration = 0.0;
         onGround = false;
         isSliding = false;
+        duration = 0;
     }
 
     void create(const Vector3&in position, const Quaternion&in rotation)
@@ -24,6 +24,27 @@ class GameObject : ScriptObject
         controls = newControls;
     }
 
+    void updateFixed(float timeStep)
+    {
+        // Disappear when duration expired
+        if (duration > 0)
+        {
+            duration -= timeStep;
+            if (duration <= 0)
+                scene.removeEntity(entity);
+        }
+    }
+
+    void handleEntityCollision(StringHash eventType, VariantMap& eventData)
+    {
+        Entity@ otherEntity = eventData["OtherEntity"].getEntity();
+        // If the other entity does not have a ScriptInstance component, it's static world geometry
+        if (!otherEntity.hasComponent("ScriptInstance"))
+            handleWorldCollision(eventData);
+        else
+            handleObjectCollision(otherEntity, eventData);
+    }
+
     void handleWorldCollision(VariantMap& eventData)
     {
         RigidBody@ body = entity.getComponent("RigidBody");
@@ -56,6 +77,9 @@ class GameObject : ScriptObject
             isSliding = false;
     }
     
+    void handleObjectCollision(Entity@ otherEntity, VariantMap& eventData)
+    {
+    }
 
     void resetWorldCollision()
     {
@@ -72,4 +96,51 @@ class GameObject : ScriptObject
             isSliding = false;
         }
     }
+
+    Entity@ spawnObject(const string&in className, const Vector3&in position, const Quaternion&in rotation)
+    {
+        Entity@ newEntity = scene.createEntity();
+
+        // Create the ScriptInstance with specified class
+        ScriptInstance@ instance = newEntity.createComponent("ScriptInstance");
+        instance.setScriptClass(cache.getResource("ScriptFile", "Scripts/NinjaSnowWar.as"), className);
+        GameObject@ object = cast<GameObject>(instance.getScriptObject());
+        if (@object != null)
+            object.create(position, rotation);
+
+        return newEntity;
+    }
+
+    Entity@ spawnParticleEffect(const string&in effectName, float duration, const Vector3&in position, const Quaternion&in rotation)
+    {
+        Entity@ newEntity = scene.createEntity();
+        
+        // Create the particle emitter
+        ParticleEmitter@ emitter = newEntity.createComponent("ParticleEmitter");
+        emitter.loadParameters(cache.getResource("XMLFile", effectName));
+        emitter.setPosition(position);
+        emitter.setRotation(rotation);
+
+        // Create a GameObject for managing the effect lifetime
+        ScriptInstance@ instance = newEntity.createComponent("ScriptInstance");
+        instance.setScriptClass(cache.getResource("ScriptFile", "Scripts/NinjaSnowWar.as"), "GameObject");
+        GameObject@ object = cast<GameObject>(instance.getScriptObject());
+        if (@object != null)
+            object.duration = duration;
+        
+        return newEntity;
+    }
+
+    void playSound(const string&in soundName)
+    {
+        RigidBody@ body = entity.getComponent("RigidBody");
+        if (@body == null)
+            return;
+        PositionalChannel@ channel = entity.createComponent("PositionalChannel", entity.getUniqueComponentName());
+        body.addChild(channel);
+        channel.setAutoRemove(true);
+        channel.setDistanceAttenuation(200, 5000, 1);
+        Sound@ sound = cache.getResource("Sound", soundName);
+        channel.play(sound, sound.getFrequency());
+    }
 }

+ 30 - 44
Bin/Data/Scripts/Ninja.as

@@ -3,15 +3,16 @@
 const int ANIM_MOVE = 1;
 const int ANIM_ATTACK = 2;
 
-const float mass = 80;
-const float friction = 0.5;
-const float moveForce = 500000;
-const float airMoveForce = 25000;
-const float dampingForce = 1000;
-const float jumpForce = 9000000;
-const Vector3 throwVelocity(0, 425, 2000);
-const Vector3 throwPosition(0, 20, 100);
-const float throwDelay = 0.1;
+const float ninjaMass = 80;
+const float ninjaFriction = 0.5;
+const float ninjaMoveForce = 500000;
+const float ninjaAirMoveForce = 25000;
+const float ninjaDampingForce = 1000;
+const float ninjaJumpForce = 9000000;
+const Vector3 ninjaThrowVelocity(0, 425, 2000);
+const Vector3 ninjaThrowPosition(0, 20, 100);
+const float ninjaThrowDelay = 0.1;
+const float ninjaDrawDistance = 15000;
 
 class Ninja : GameObject
 {
@@ -53,7 +54,7 @@ class Ninja : GameObject
         AnimatedModel@ model = entity.createComponent("AnimatedModel");
         model.setModel(cache.getResource("Model", "Models/Ninja.mdl"));
         model.setMaterial(cache.getResource("Material", "Materials/Ninja.xml"));
-        model.setDrawDistance(15000);
+        model.setDrawDistance(ninjaDrawDistance);
         model.setCastShadows(true);
 
         // Create animation controller
@@ -65,8 +66,8 @@ class Ninja : GameObject
         body.setPosition(position);
         body.setRotation(rotation);
         body.setMode(PHYS_DYNAMIC);
-        body.setMass(mass);
-        body.setFriction(friction);
+        body.setMass(ninjaMass);
+        body.setFriction(ninjaFriction);
         body.setCollisionShape(cache.getResource("CollisionShape", "Physics/Ninja.xml"));
         body.setCollisionGroup(1);
         body.setCollisionMask(3);
@@ -90,7 +91,7 @@ class Ninja : GameObject
         return q;
     }
 
-    void updateFixed(float time)
+    void updateFixed(float timeStep)
     {
         RigidBody@ body = entity.getComponent("RigidBody");
         AnimationController@ controller = entity.getComponent("AnimationController");
@@ -118,12 +119,12 @@ class Ninja : GameObject
         if (onGround)
         {
             inAirTime = 0;
-            onGroundTime += time;
+            onGroundTime += timeStep;
         }
         else
         {
             onGroundTime = 0;
-            inAirTime += time;
+            inAirTime += timeStep;
         }
 
         if ((inAirTime < 0.3f) && (!isSliding))
@@ -154,7 +155,7 @@ class Ninja : GameObject
                 }
                 // Normalize so that diagonal strafing isn't faster
                 force.normalize();
-                force *= moveForce;
+                force *= ninjaMoveForce;
                 body.applyForce(force);
                 
                 // Walk or sidestep animation
@@ -170,7 +171,7 @@ class Ninja : GameObject
             }
 
             // Overall damping to cap maximum speed
-            body.applyForce(Vector3(-dampingForce * vel.x, 0, -dampingForce * vel.z));
+            body.applyForce(Vector3(-ninjaDampingForce * vel.x, 0, -ninjaDampingForce * vel.z));
 
             // Jumping
             if (controls.isDown(CTRL_JUMP))
@@ -179,7 +180,7 @@ class Ninja : GameObject
                 {
                     // Lift slightly off the ground for better animation
                     body.setPosition(body.getPhysicsPosition() + Vector3(0, 3, 0));
-                    body.applyForce(Vector3(0, jumpForce, 0));
+                    body.applyForce(Vector3(0, ninjaJumpForce, 0));
                     inAirTime = 1.0f;
                     controller.setAnimation("Models/Ninja_JumpNoHeight.ani", ANIM_MOVE, false, true, 1.0, 1.0, 0.0, 0.0, true);
                     okToJump = false;
@@ -206,7 +207,7 @@ class Ninja : GameObject
                         force += q * Vector3(1, 0, 0);
                     // Normalize so that diagonal strafing isn't faster
                     force.normalize();
-                    force *= airMoveForce;
+                    force *= ninjaAirMoveForce;
                     body.applyForce(force);
                 }
             }
@@ -218,32 +219,23 @@ class Ninja : GameObject
         
         // Shooting
         if (throwTime >= 0)
-            throwTime -= time;
+            throwTime -= timeStep;
         
         if ((controls.isPressed(CTRL_FIRE, prevControls)) && (throwTime <= 0))
         {
-            Vector3 projectileVel = getAim() * throwVelocity;
-            
+            Vector3 projectileVel = getAim() * ninjaThrowVelocity;
+
             controller.setAnimation("Models/Ninja_Attack1.ani", ANIM_ATTACK, false, true, 1.0, 0.75, 0.0, 0.0, false);
             controller.setFade("Models/Ninja_Attack1.ani", 0.0, 0.5);
             controller.setPriority("Models/Ninja_Attack1.ani", 1);
-            
-            /*
-            // Do not spawn object for clientside prediction, only animate for now
-            if (!isProxy())
-            {
-                SnowBall* obj = spawnObject<SnowBall>("Snowball");
-                obj->create(getBody()->getPhysicsPosition() + time * vel + q * tThrowPosition, getAim());
-                obj->setSide(mSide);
-                obj->setOrigin(getEntity()->getID());
-                obj->getBody()->setLinearVelocity(projectileVel);
 
-                //! \todo the throw sound should be instant, and therefore predicted on client
-                playSound("Sounds/NutThrow.wav");
-            }
-            */
+            Entity@ snowball = spawnObject("SnowBall", body.getPhysicsPosition() + vel * timeStep + q * ninjaThrowPosition, getAim());
+            RigidBody@ snowballBody = snowball.getComponent("RigidBody");
+            snowballBody.setLinearVelocity(projectileVel);
             
-            throwTime = throwDelay;
+            playSound("Sounds/NutThrow.wav");
+
+            throwTime = ninjaThrowDelay;
         }
         
         prevControls = controls;
@@ -251,11 +243,5 @@ class Ninja : GameObject
         resetWorldCollision();
     }
     
-    void handleEntityCollision(StringHash eventType, VariantMap& eventData)
-    {
-        Entity@ otherEntity = eventData["OtherEntity"].getEntity();
-        // If the other entity does not have a ScriptInstance component, it's static world geometry
-        if (!otherEntity.hasComponent("ScriptInstance"))
-            handleWorldCollision(eventData);
-    }
+
 }

+ 6 - 5
Bin/Data/Scripts/NSWScript.as → Bin/Data/Scripts/NinjaSnowWar.as

@@ -1,6 +1,7 @@
 // Partial remake of NinjaSnowWar in script
 
 #include "Scripts/Ninja.as"
+#include "Scripts/SnowBall.as"
 
 Scene@ gameScene;
 Camera@ gameCamera;
@@ -68,7 +69,7 @@ void initConsole()
 
 void initScene()
 {
-    @gameScene = engine.createScene("NSWScript", BoundingBox(-100000.0, 100000.0), 8, true);
+    @gameScene = engine.createScene("NinjaSnowWar", BoundingBox(-100000.0, 100000.0), 8, true);
 
     File@ levelFile = cache.getFile("TestLevel.xml");
     gameScene.loadXML(levelFile);
@@ -141,9 +142,9 @@ void createOverlays()
 
 void spawnPlayer()
 {
-    Entity@ playerEntity = gameScene.createEntity("ObjPlayer");
+    Entity@ playerEntity = gameScene.createEntity("Player");
     ScriptInstance@ instance = playerEntity.createComponent("ScriptInstance");
-    instance.setScriptClass(cache.getResource("ScriptFile", "Scripts/NSWScript.as"), "Ninja");
+    instance.setScriptClass(cache.getResource("ScriptFile", "Scripts/NinjaSnowWar.as"), "Ninja");
     Ninja@ object = cast<Ninja>(instance.getScriptObject());
     object.create(Vector3(0, 90, 0), Quaternion());
 }
@@ -202,7 +203,7 @@ void updateControls()
     playerControls.pitch += mouseSensitivity * input.getMouseMoveY();
     playerControls.pitch = clamp(playerControls.pitch, -60, 60);
 
-    Entity@ playerEntity = gameScene.getEntity("ObjPlayer");
+    Entity@ playerEntity = gameScene.getEntity("Player");
     if (@playerEntity == null)
         return;
 
@@ -213,7 +214,7 @@ void updateControls()
 
 void updateCamera()
 {
-    Entity@ playerEntity = gameScene.getEntity("ObjPlayer");
+    Entity@ playerEntity = gameScene.getEntity("Player");
     if (@playerEntity == null)
         return;
 

+ 73 - 0
Bin/Data/Scripts/SnowBall.as

@@ -0,0 +1,73 @@
+#include "Scripts/GameObject.as"
+
+const float snowballMass = 10;
+const float snowballFriction = 0.5;
+const float snowballDampingForce = 20;
+const float snowballMinHitSpeed = 100;
+const float snowballDuration = 5;
+const float snowballGroundHitDuration = 1;
+const float snowballDrawDistance = 7500;
+
+class SnowBall : GameObject
+{
+    SnowBall()
+    {
+        duration = snowballDuration;
+    }
+
+    void start()
+    {
+        subscribeToEvent("EntityCollision", "handleEntityCollision");
+    }
+
+    void create(const Vector3&in position, const Quaternion&in rotation)
+    {
+        // Create model
+        StaticModel@ model = entity.createComponent("StaticModel");
+        model.setModel(cache.getResource("Model", "Models/SnowBall.mdl"));
+        model.setMaterial(cache.getResource("Material", "Materials/Snow.xml"));
+        model.setDrawDistance(snowballDrawDistance);
+        model.setCastShadows(true);
+    
+        // Create body
+        RigidBody@ body = entity.createComponent("RigidBody");
+        body.setPosition(position);
+        body.setRotation(rotation);
+        body.setMode(PHYS_DYNAMIC);
+        body.setMass(snowballMass);
+        body.setFriction(snowballFriction);
+        body.setCollisionShape(cache.getResource("CollisionShape", "Physics/SnowBall.xml"));
+        body.setCollisionGroup(1);
+        body.setCollisionMask(3);
+        body.addChild(model);
+    }
+    
+    void updateFixed(float timeStep)
+    {
+        // Apply damping when rolling on the ground, or near disappearing
+        RigidBody@ body = entity.getComponent("RigidBody");
+        Vector3 vel = body.getLinearVelocity();
+        if ((onGround) || (duration < snowballGroundHitDuration))
+            body.applyForce(Vector3(-snowballDampingForce * vel.x, 0, -snowballDampingForce * vel.z));
+            
+        // Disappear when duration expired
+        if (duration > 0)
+        {
+            duration -= timeStep;
+            if (duration <= 0)
+            {
+                spawnParticleEffect("Particle/SnowExplosion.xml", 1, body.getPhysicsPosition(), Quaternion());
+                scene.removeEntity(entity);
+            }
+        }
+    }
+    
+    void handleWorldCollision(VariantMap& eventData)
+    {
+        GameObject::handleWorldCollision(eventData);
+        
+        // If hit the ground, disappear after a short while
+        if (duration > snowballGroundHitDuration)
+            duration = snowballGroundHitDuration;
+    }
+}

+ 1 - 1
Bin/NSWScript.bat

@@ -1 +1 @@
-Urho3D Scripts/NSWScript.as
+Urho3D Scripts/NinjaSnowWar.as

+ 1 - 3
Engine/Script/ScriptEngine.cpp

@@ -34,8 +34,6 @@
 
 #include "DebugNew.h"
 
-static const int GARBAGE_COLLECT_ITERATIONS = 5;
-
 void messageCallback(const asSMessageInfo* msg, void* param)
 {
     ScriptEngine* engine = static_cast<ScriptEngine*>(param);
@@ -135,7 +133,7 @@ void ScriptEngine::garbageCollect(bool fullCycle)
         mAngelScriptEngine->GarbageCollect(asGC_FULL_CYCLE);
     else
     {
-        for (int i = 0; i < GARBAGE_COLLECT_ITERATIONS; ++i)
+        for (unsigned i = 0; i < GARBAGE_COLLECT_ITERATIONS; ++i)
             mAngelScriptEngine->GarbageCollect(asGC_ONE_STEP);
     }
 }

+ 3 - 0
Engine/Script/ScriptEngine.h

@@ -40,6 +40,9 @@ enum ScriptLogMode
 //! Maximum function/method nesting level
 static const unsigned MAX_SCRIPT_NESTING_LEVEL = 32;
 
+//! Amount of garbage collection iterations when not doing a full cycle
+static const unsigned GARBAGE_COLLECT_ITERATIONS = 5;
+
 //! Utilizes the AngelScript library for executing scripts
 class ScriptEngine : public RefCounted
 {

+ 2 - 2
Engine/Script/ScriptFile.cpp

@@ -220,7 +220,7 @@ asIScriptObject* ScriptFile::createObject(const std::string& className)
     if (scriptNestingLevel >= MAX_SCRIPT_NESTING_LEVEL)
     {
         LOGERROR("Maximum script execution nesting level exceeded, can not create object");
-        return false;
+        return 0;
     }
     
     asIScriptContext* context = mScriptEngine->getScriptFileContext(scriptNestingLevel);
@@ -229,7 +229,7 @@ asIScriptObject* ScriptFile::createObject(const std::string& className)
     if (!type)
         return 0;
     
-    // Ensure that the type implements the "ScriptObject" interface, so it can be returned also to script properly
+    // Ensure that the type implements the "ScriptObject" interface, so it can be returned to script properly
     bool found = false;
     std::map<asIObjectType*, bool>::const_iterator i = mCheckedClasses.find(type);
     if (i != mCheckedClasses.end())