Paradox Game Engine Tutorial Part 5: Controlling Scenes Programmatically

 

In this tutorial in our ongoing Paradox3d Game Engine tutorial series  we are going to look at controlling a Paradox game engine scene programmatically.  This includes accessing entities created in the editor, creating new entities, loading assets and more.  It should give you a better idea of the relationship between the scene and your code.

 

As always there is an HD video available here.

 

Creating a Simple Script

 

As part of this process we are going to be attaching a script to a scene entity programmatically.  First we need that script to be created.  We covered this process back in this tutorial if you need a brush up.  We are going to create an extremely simple script named BackAndForth.cs, which simply moves the entity back and forth along the x-axis in our scene.  Here is the contents of the script:

using System;  using SiliconStudio.Paradox.Engine;    namespace SceneSelect  {      public class BackAndForth : SyncScript      {          private float currentX = 0f;          private const float MAX_X = 5f;          bool goRight = false;          public override void Update()          {              if (Game.IsRunning)              {                  if (goRight)                  {                      currentX += 0.1f;                   }                  else                  {                      currentX -= 0.1f;                  }                    if (Math.Abs(currentX) > MAX_X)                      goRight = !goRight;                    Entity.Transform.Position.X = currentX;              }          }      }  }  

 

If you’ve gone through the previous tutorials, this script should require no explanation.  We simply needed an example script that we can use later on.  This one merely moves the attached entity back and forth across the X axis until it reaches + or – MAX_X.

 

Now what we want to do is attach this script to the Sphere entity created in the default scene.  This means we are going to need to be able to locate an entity in code, and perhaps more importantly, we need some code to run.  We could create our own custom Game class like we did last tutorial, but this time we are going to do things a bit different.  Instead we are going to create a StartupScript.

 

First we new to create a new empty Entity in our scene to attach the script component to.  I called mine Config:

image

Next we create the Script we are going to attach.  Start with the following extremely simple script, Startup.cs:

using System;  using System.Collections.Generic;  using System.Linq;  using System.Text;  using System.Threading.Tasks;  using SiliconStudio.Paradox.Engine;  using SiliconStudio.Paradox.Rendering;    namespace SceneSelect  {      public class Startup : StartupScript      {          public override void Start()          {              base.Start();          }      }  }  

 

A StartupScript is a type of script that is loaded, as you may guess, on start. Unlike the Sync/AsyncScript classes we used earlier, there is no per frame update callback occurring.  This makes StartupScripts very useful for exactly this type of configuration tasks.

 

Now that we have our script, let’s attach it to our entity:

image

 

Finding an Entity using Code

 

First we are going to look at the process of locating an entity creating in Paradox Studio using code.  The following code will select the Sphere from the default scene using LINQ.

    var sphere = (from entities in this.SceneSystem.SceneInstance                      where entities.Components.ContainsKey(ModelComponent.Key)                      select entities).FirstOrDefault();  

You can get the currently active scene using SceneSystem.SceneInstance, which contains a simple collection of Entity objects.  We then filter by entities with Components of type ModelComponent.  There are many ways we could have accomplished the same thing.  This query actually returns all entities in the scene that have a ModelComponent attached, which is overkill.  We could also select by the entities Name attribute:

image

Using the code:

    var sphere = (from entities in this.SceneSystem.SceneInstance                      where entities.Name == "Sphere" select entities).                                           FirstOrDefault();      if (sphere == null) return;  

 

Attaching a Script Component Programmatically

 

    ScriptComponent scriptComponent = new ScriptComponent();      scriptComponent.Scripts.Add(new BackAndForth());      sphere.Components.Add<ScriptComponent>(ScriptComponent.Key, scriptComponent);  

 

Now that we have a reference to our sphere Entity, adding a new component is pretty simple.  Remember that the ScriptComponent is a collection of Script objects.  Simply Add() an instance of our newly created BackAndForth script.  Finally attach a ScriptComponent to our Sphere’s Components collection. 

 

When we run this code we will see:

BackAndForth

 

Creating a new Entity

 

We can also create another entity programmatically.

    Entity entity = new Entity(position: new SiliconStudio.Core.Mathematics.                      Vector3(0, 0, 1), name: "MyEntity");      var model = (SiliconStudio.Paradox.Rendering.Model)Asset.Get(typeof(                  SiliconStudio.Paradox.Rendering.Model), "Sphere");      ModelComponent modelComponent = new ModelComponent(model);      entity.Add<ModelComponent>(ModelComponent.Key, modelComponent);  

 

Here we create a new entity with the name “MyEntity” and set it’s location to (0,0,1).  Next we get a reference to the ProceduralModel created in Paradox Studio, with a call to Asset.Get() specifying the type and URL ( you can see the Url value by mouse overing the asset in the Asset Viewer panel in Studio).  Now we create a new ModelComponent using this Model.  (Keep in mind, changes to it the Model will affect all instances, as I will show momentarily).  Finally we add the ModelComponent to the entity.

Finally we add our newly created entity to the scene using:

    SceneSystem.SceneInstance.Scene.AddChild(entity);  

Now when we run the code:

BackAndForth2

 

As I mentioned earlier, changes to the Model will affect all instances.  For example, let’s say we create a new Material in the editor and apply it to the model.

image

Now the code:

    Entity entity = new Entity(position: new SiliconStudio.Core.Mathematics.                      Vector3(0, 0, 1), name: "MyEntity");      var model = (SiliconStudio.Paradox.Rendering.Model)Asset.Get(typeof(                  SiliconStudio.Paradox.Rendering.Model), "Sphere");      var material = Asset.Load<Material>("MyMaterial");      model.Materials.Clear();      model.Materials.Add(new MaterialInstance(material));      ModelComponent modelComponent = new ModelComponent(model);      entity.Add<ModelComponent>(ModelComponent.Key, modelComponent);  

And the (non-animated) result:

image

As you can see, the material on all of the Spheres has been replaced.  If you do not want this behaviour, you will have to create a new Model, either in Studio or programmatically.

 

New Entity using Clone

 

We could have also created our entity using the Clone() method of our existing Entity.

    var anotherSphere = sphere.Clone();      sphere.Transform.Position.Z = 1f;      SceneSystem.SceneInstance.Scene.AddChild(anotherSphere);  

Keep in mind, the close will get all of the components of the cloned Entity, so if we clone after we add the ScriptComponent, it will also have the script attached.

 

 

Our complete source example:

using System.Linq;  using SiliconStudio.Paradox.Engine;  using SiliconStudio.Paradox.Rendering;    namespace SceneSelect  {      public class Startup : StartupScript      {          public override void Start()          {              base.Start();                var sphere = (from entities in this.SceneSystem.SceneInstance                           where entities.Components.ContainsKey(ModelComponent.                                                                 Key)                           select entities).FirstOrDefault();              //var sphere = (from entities in this.scenesystem.sceneinstance              //              where entities.name == "sphere" select entities).                                                   firstordefault();              //if (sphere == null) return;                ScriptComponent scriptComponent = new ScriptComponent();              scriptComponent.Scripts.Add(new BackAndForth());              sphere.Components.Add<ScriptComponent>(ScriptComponent.Key,                                                      scriptComponent);                  Entity entity = new Entity(position: new SiliconStudio.Core.                              Mathematics.Vector3(0, 0, 1), name: "MyEntity");              var model = (SiliconStudio.Paradox.Rendering.Model)Asset.Get(typeof(                          SiliconStudio.Paradox.Rendering.Model), "Sphere");              var material = Asset.Load<Material>("MyMaterial");              model.Materials.Clear();              model.Materials.Add(new MaterialInstance(material));              ModelComponent modelComponent = new ModelComponent(model);              entity.Add<ModelComponent>(ModelComponent.Key, modelComponent);                SceneSystem.SceneInstance.Scene.AddChild(entity);                var anotherSphere = sphere.Clone();              sphere.Transform.Position.Z = 1f;              SceneSystem.SceneInstance.Scene.AddChild(anotherSphere);          }      }  }  

And, running:

BackAndForth3

 

The Video

 

Programming Paradox


Scroll to Top