Jump to content
  • entries
    948
  • comments
    5,905
  • views
    946,581

Unit Testing


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

37 Comments


Recommended Comments



Rick

Posted

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 :)

YouGroove

Posted

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.

Pixel Perfect

Posted

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!

Josh

Posted

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
YouGroove

Posted

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 ?

Shaping

Posted

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.

MCP

Posted

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?

Josh

Posted

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
Canardia

Posted

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?

Josh

Posted

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.

YouGroove

Posted

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.

Josh

Posted

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".


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...