Jump to content
  • entries
    945
  • comments
    5,899
  • views
    930,190

Unit Testing


Josh

8,727 views

 Share

I've spent the last few days writing simple examples for every single command in Leadwerks 3. Not only does this make the documentation more friendly, it also acts as a final test to make sure all the commands work the way they say they should. I make the C++ example and then Chris converts it to Lua (and tells me what I did wrong!).

 

I didn't realize it at first, but this really showcases the strength of API design of Leadwerks. Since you get full control over the execution and flow of a Leadwerks program, it's easy to learn from simple examples that demonstrate one idea. Below are a few examples for different commands in the API.

 

Get the device accellerometer reading:

#include "App.h"

using namespace Leadwerks;

Window* window = NULL;
Context* context = NULL;

bool App::Start()
{
   window = Window::Create();
   context = Context::Create(window);
   return true;
}

bool App::Continue()
{
   if (window->Closed() || window->KeyDown(Key::Escape)) return false;

   Draw::SetColor(0,0,0);
   context->Clear();

   //Display the device information on the screen
   Draw::SetBlendMode(Blend::Alpha);
   Draw::SetColor(1,1,1);
   Draw::Text("Orientation: "+String(Device::GetOrientation()),2,2);
   Draw::Text("Acceleration: "+Device::GetAcceleration().ToString(),2,22);
   Draw::SetBlendMode(Blend::Solid);

   context->Sync();        
   return true;
}

 

Create a physics shape from a model and use it on a scaled entity :D :

#include "App.h"

using namespace Leadwerks;

Window* window = NULL;
Context* context = NULL;
World* world = NULL;
Camera* camera = NULL;

bool App::Start()
{
   window = Window::Create();
   context = Context::Create(window);
   world = World::Create();
   camera = Camera::Create();
   camera->SetRotation(35,0,0);
   camera->Move(0,0,-10);
   Light* light = DirectionalLight::Create();
   light->SetRotation(35,35,0);

   //Create the ground
   Model* ground = Model::Box(10,1,10);
   ground->SetPosition(0,-0.5,0);
   ground->SetColor(0,1,0);

   //Create a shape
   Shape* shape = Shape::Box(0,0,0, 0,0,0, 10,1,10);
   ground->SetShape(shape);
   shape->Release();

   //Load a model
   Model* model = Model::Load("Models/teapot.mdl");
   model->SetPosition(0,0,0);
   model->SetColor(0,0,1);
   model->SetScale(4,4,4);

   //Create a shape
   shape = Shape::PolyMesh(model->GetSurface(0));
   model->SetShape(shape);
   model->SetPosition(0,0,0);
   shape->Release();

   //Create some objects to fall
   model = Model::Sphere();
   shape = Shape::Sphere();
   model->SetShape(shape);
   shape->Release();
   model->SetMass(1);
   model->SetColor(Math::Rnd(0,1),Math::Rnd(0,1),Math::Rnd(0,1));
   model->SetPosition(Math::Rnd(-1,1),Math::Rnd(3,6),Math::Rnd(-1,1));

   for (int i=0; i<10; i++)
   {
       model = (Model*)model->Instance();
       model->SetCollisionType(Collision::Prop);
       model->SetColor(Math::Rnd(0,1),Math::Rnd(0,1),Math::Rnd(0,1));
       model->SetPosition(Math::Rnd(-1,1),5+i*2,Math::Rnd(-1,1));
   }

   return true;
}

bool App::Continue()
{
   if (window->Closed() || window->KeyDown(Key::Escape)) return false;

   Time::Update();
   world->Update();
   world->Render();
   context->Sync();

   return true;
}

 

Or in Lua, if you prefer:

function App:Start()

 

self.window=Window:Create(self.title,0,0,1136+6,640+32,Window.Titlebar+Window.Center+8)

self.context=Context:Create(self.window,0)

self.world=World:Create()

camera = Camera:Create()

camera:SetRotation(35,0,0)

camera:Move(0,0,-10)

light = DirectionalLight:Create()

light:SetRotation(35,35,0)

 

--Create the ground

ground = Model:Box(10,1,10)

ground:SetPosition(0,-.05,0)

ground:SetColor(0,1,0)

 

--Create a shape

shape = Shape:Box(0,0,0, 0,0,0, 10,1,10)

ground:SetShape(shape)

shape:Release()

 

--Load a model

model = Model:Load("Models/teapot.mdl")

model:SetPosition(0,0,0)

model:SetColor(0,0,1)

model:SetScale(4,4,4)

 

--Create a shape

shape = Shape:PolyMesh((model:GetSurface(0)))

model:SetShape(shape)

model:SetPosition(0,0,0)

shape:Release()

 

--Create some objects to fall

model = Model:Sphere()

shape = Shape:Sphere()

model:SetShape(shape)

shape:Release()

model:SetMass(1)

model:SetColor(Math:Rnd(0,1),Math:Rnd(0,1))

model:SetPosition(Math:Rnd(-1,1),Math:Rnd(3,6),Math:Rnd(-1,1),true)

 

for i=0, 9 do

model = tolua.cast(model:Instance(),"Model")

model:SetCollisionType(Collision.Prop)

model:SetColor(Math:Rnd(0,1),Math:Rnd(0,1),Math:Rnd(0,1))

model:SetPosition(Math:Rnd(-1,1),5+i*2,Math:Rnd(-1,1),true)

end

return true

end

 

function App:Continue()

 

if self.window:Closed() or self.window:KeyHit(Key.Escape) then return false end

 

Time:Update()

self.world:Update()

self.world:Render()

self.context:Sync()

 

return true

end

 

Create a texture from scratch:

#include "App.h"

using namespace Leadwerks;

Window* window = NULL;
Context* context = NULL;
World* world = NULL;
Texture* texture = NULL;

bool App::Start()
{
   window = Window::Create();
   context = Context::Create(window);

   //Create a texture
   texture = Texture::Create(256,256);

   //Set the texture pixel data
   char* pixels = (char*)malloc(texture->GetMipmapSize(0));
   char r,g,b;
   for (int x=0; x<256; x++)
   {
       for (int y=0; y<256; y++)
       {
           int p = (x*texture->GetWidth() + y)*4;
           memcpy(&r,pixels + p + 0, 1);
           memcpy(&g,pixels + p + 1, 1);
           memcpy(&b,pixels + p + 2, 1);
           if (x<128)
           {
               if (y<128)
               {
                   r=0; g=0; b=255;
               }
               else
               {
                   r=255; g=0; b=0;
               }
           }
           else
           {
               if (y<128)
               {
                   r=255; g=0; b=0;
               }
               else
               {
                   r=0; g=0; b=255;
               }                
           }
           memcpy(pixels + p + 0, &r, 1);
           memcpy(pixels + p + 1, &g, 1);
           memcpy(pixels + p + 2, &b, 1);
       }
   }
   texture->SetPixels(pixels);

   return true;
}

bool App::Continue()
{
   if (window->Closed() || window->KeyDown(Key::Escape)) return false;

   Draw::SetColor(0,0,0);
   context->Clear();

   //Display the texture on screen
   Draw::SetColor(1,1,1);
   Draw::Image(texture,0,0);

   context->Sync();        
   return true;
}

  • Upvote 5
 Share

37 Comments


Recommended Comments



There is no excuse in good programming practice to not use a faster method when it is as easy to implement as a slow method. When systems grow bigger, all the little speed differences will exponentially sum of, when one function is calling another function multiple times per loop.

 

What happens when speed programming practice meet maintainability programming practice? They don't always mix well. Who wins :)

Link to comment

It depends on the loop also, if you ahve only a loop with 5 iterations i'm not sure this will impact a lot per frame.

For Lua, this is some interpredted language, so this sort of optimisation i think will matter lot more.

Link to comment

It's a law of diminishing returns. Normally far better optimization is achieved by optimizing your own code routines after profiling your code to see where the real time is being spent. It may never need optimizing, that is a real possibility if your code is written well. As already stated the gains are very small for the types of optimizations Canardian is proposing, especially in a game engine where so much time is spend in rendering cycles etc! Nothing wrong with implementing these things from the start but if you haven't its not really worth going back over your code.

 

Generally speaking, imho unless absolute performance is critical maintainability should always win out over performance practice. For most applications CPU cycles are in abundance!

Link to comment

You should think about optimizing your optimization effort. You have a finite amount of effort and time. Are you going to spend it where it will be effective, or spend all your time worrying about an iterator that doesn't even run in the program loop?

 

It gets back on topic, or else it gets the hose again.

  • Upvote 2
Link to comment

You'll have a bigger optimization by removing non visible obects to draw, culling ,texture size, shadows optimisation and distance : these optimisation REALLY impact a lot your game more than simple C++ maths that don't use Trigonometry.

 

I think we could put all C++ optimizing ideas posted here in some WIKI special : C++ code optimisation no ?

Link to comment

Josh, is the matching C API also being prepared?

 

Specifically, do you have a .c file and .h file I can compile to a .dll file on Windows, and interface to with my favorite dynamic language? Syntactically, I am assuming that the form of each function name will be very similar, each being prefixed by an appropriate class name. I can then make my own classes and name-spaces in my dev environ, as needed to encapsulate the C API.

 

This is exciting. I look forward to a test run with the new C API.

Link to comment

It's good to know you are putting a lot of effort into the LE3 documentation Josh.

Is there much left to do before the official launch?

Link to comment

We're about 40% through the code examples. The last thing will be some level design work so our characters have somewhere nice to run around in.

  • Upvote 2
Link to comment

Can they also jump/climb/crouch over/under obstacles? Is there any way to influence the pathfinding process, or is it always hardcoded like go from point A to point B?

Link to comment

They can can't crouch, but they will climb over or go around dynamic obstacles and avoid each other. You just tell them to go to a point, or follow an entity, and you can specify the speed and acceleration.

Link to comment

I agree, advanced AI should be done by community in the form of some plugin template, for example you could find some Gears of Wars like AI with the ability to take cover, jump over little obstacles , and run with a special camera.

I remeber using some old engine having a good editor, and containing all IA , but it was buggy , not maintained and was not enought customizable.

This is not to the engine to give advanced AI stuff i think, i eally prefer that way of having only a basic features like telling to go from point A to B.

And it will be to users to create advanced AI systems of any genre.

Link to comment

That kind of stuff isn't so much a matter of programming as it is a matter of decision-making. Exactly what do you want your AI to do, and how do you define that?

 

A simple attack mode would be "move towards the player until you have a clear line of sight, then start shooting".

Link to comment

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...