2014

Archive for 2014

Making an engine to run with custom game code

So, I'm working on my game and decided to share some simplified version of my code here. I'm making a game engine in javascript, and needed some way to code some actions.

The code is here, you can save it as example.html (http://jsfiddle.net/e3b0kocc/):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
    <!DOCTYPE html> <html> <body> <script>
    var engine={}, actions={};
    engine.atomStack=new Array();

    engine.runatomStack = function(){
        while(engine.atomStack.length > 0){
            var actionToRun = engine.atomStack.shift();
            actionToRun[0](actionToRun[1]);
        } 
    };

    engine.action1 = function( param ) {
        //execute something param[0]
        console.log("executed action 1, param " + param[0] ); }

    engine.action2 = function( param ) {
        //execute something param[0] and param[1]
        console.log("executed action 2, params " + param[0] + " " + param[1] ); }

    actions.action1 = function( param ) {
        //do something param[0]
        var params = param.split(';');
        engine.atomStack.push([engine.action1,params]); }

    actions.action2 = function( param ) {
        //do something param[0] and param[1]
        var params = param.split(';');
        params[1]=parseInt(params[1],10)+2
        engine.atomStack.push([engine.action2,params]); }

    translateActions = function(action, param) {    
        actions[action](param); };

    eventActivate = function(event) {
        for (var i = 0; i < events[event].length ; i++) {
            var action = events[event][i];
            var actionAndParam = action.split('|');
            translateActions(actionAndParam[0],actionAndParam[1]);
        }
    };

    events = {
        1: ["action1|5","action2|2;2","action1|2"],
        2: ["action2|5;2","action2|2;2"],
        3: ["action2|5;2","action1|2"] };
    </script> </body> </html>

Something happens, and I need to run the actions inside an event. I call eventActivate passing the event that should happen. The function translateAction read this information and calls the function that set up the actions. My logic is based that a level contain events, an event can contain actions, and each different action contain atoms.

So, for example, at some point you call eventActivate(1) and that will push the relative events on the stack. Then from time to time the engine is used and calls engine.runatomStack() to execute whatever is there. Below is an example using f12 to call developer console and insert javascript (used Firefox).

//engine.atomStack is Array [  ]

eventActivate(2)
//engine.atomStack is Array [ Array[2], Array[2] ]

engine.runatomStack()

//prints:
//   "executed action 2, params 5 4" example.html:18
//   "executed action 2, params 2 4" example.html:18

//engine.atomStack is Array [  ]


I decided to make this post because I asked a question on stackoverflow and had to make a simpler version of my code to show there and thought that it could be useful to show here too!

Easy way to test a tiled and animated background sprite

I'm working in my game, it's going be awesome. Ok, so while working in it, I need a fast way to test my animated background sprites. I use Aseprite for drawing - it's great, and if you use Ubuntu, you can  sudo apt-get install aseprite. Go to the website for more info on other OS: http://www.aseprite.org/

Let's try an example:

save this water sprite! I made it myself! :D (that's why it's ugly..)

Aseprite can export your sprite sheet to gif easily:
file->import sprite sheet
Choose your file, and set width and height to the sprite size (mine is 32 x 32 pixels)
Now in frame->play animation you can see your animation is working!
Now in file->save as... select .gif and we have a beautiful animated gif.


This is the gif!

Ok, problem is it's difficult to have an idea if the water will turn out ok because we haven't seem it animated AND tiled. So how can we do this? I was thinking about it and decided to code an easy solution to the viewing problem! Let's make and html!

 <html>  
 <head>  
 <title>Animated Background Tile Tester</title>  
 <style TYPE="text/css">  
 <!-- body {  
 background-image: url("animateWater1.gif");  
 background-size: 64px 64px;  
 image-rendering: -moz-crisp-edges;  
 image-rendering: -o-crisp-edges;  
 image-rendering: -webkit-optimize-contrast;  
 -ms-interpolation-mode: nearest-neighbor;  
 } -->  
 </style>  
 </head>  
 <body></body>   
 </html>  

Ok, so this is a very simple html for testing sprite. Let's break it down:
background-image is where the filename of your image goes.
background-size is the size. I'm using it to double the scale of my gif, because my game will use double pixel scale.
image-rendering lines and -ms-interpolation are forcing use nearest-neighbor for aliasing option - like no anti-aliasing.


I couldn't find a way to embedded the example above in this blog post, if you know how to do the Xzibit way and put a html page inside a html page, please tell me.

Nearest-neighbor doesn't work

Ok, I've only tested in Firefox 31.0. I know this example doesn't work in Chrome. I'm using because I had to disable caching - I use SSD and there is a lot of people on the web saying that caching is bad for it. Don't know if it's true, but with no cache it's better for web development.

I can't resize Aseprite on Ubuntu!!!

Yeah, unfortunately this is true for the Aseprite in the repository. But there is one way out. Close it. In your home folder, find the .asepriterc file and open it. Find the lines that look like this:

 ...  
 [GfxMode]  
 Maximized = no  
 Width = 1800  
 Height = 960  
 ...  

And change the width height to one of your choice, and then reopen Aseprite. It's not the best solution, but it's easy and works! There are other solutions in the web, Google is your friend.



A tilemap in PyQt for a bigger game project



I wanted to get back into game development, as a hobby, for some time now. I have some idea of what I want: a simple 2d rpg game, like Pokemon in Game Boy. So for starts, I needed a way to draw tilemaps, a good editor, something I could customize for my needs.

But I really couldn't find anything as easy to use as I wanted so I decided to write my own. I opted to use Python and PyQt - and I don't know much about any of them, so I had a slow start. I'm reading about PyQt for two days in all my free time.

Until now I just have a window showing a hardcoded tilemap on screen. I'm sharing it, even unfinished, since I had a really hard time figuring out the steps to do this.

Since python cares about identation, I'm sharing a link: http://pastebin.com/d1FGDCJf

 #!/usr/bin/env python  
 # display a tiled image from tileset with PyQt  
 import sys  
 from PIL import Image  
 from PIL.ImageQt import ImageQt  
 from PyQt4 import QtGui, QtCore  
 from PyQt4.QtGui import QImage  
 from numpy import ndarray  
 # Simple background, will use open from a file in future  
 background =   [[12, 2,12, 2, 1, 2, 3, 3, 1, 1],  
      [ 1, 1, 7, 8, 5, 6, 7, 8,12, 3],  
      [ 1, 3, 1, 3,12,10,10, 1,12,12],  
      [ 2,12, 0, 4,10, 3,12, 2,12,12],  
      [12,12, 1, 1,10, 3,12, 2,12, 1],  
      [12,12,12, 0,10, 2, 1,12, 1,12],  
      [ 3,12, 3,12, 0, 2, 2,12,12, 3],  
      [ 1,12, 1,12, 1, 1,12,12, 3,12],  
      [ 3,12, 0,12,12,12,12,12, 3, 3],  
      [12, 3, 1, 2, 3,12,12,12, 1,12]]  
 # This will have the tileset  
 tileset = []   
 # last time I was writing this save function!  
 def save():  
   f = open( "map.txt" , "wb" )  
   f.write( "background :   [" )  
   for i in range(len(background) ):  
         f.write( "[" )  
     for j in range(len(background[0])):  
       f.write( str(background[j][i]) )  
       f.write( "," ) if j != len(background[0])-1 else (f.write( "]," ) if i != len(background)-1 else f.write( "]" ))  
     f.write( "\n" ) if i != len(background)-1 else f.write( "]" )  
   f.close()  
 class MyImage(QtGui.QWidget):  
   def __init__(self, parent, width, height):  
     QtGui.QWidget.__init__(self, parent)  
     BOX_SIZE = 32  
     image_file = Image.open("simpletile.png")  
     self.setWindowTitle("View tiled background")  
     # get tileset file and split it in images that can be pointed through array  
     if image_file.size[0] % BOX_SIZE == 0 and image_file.size[1] % BOX_SIZE ==0 :  
       currentx = 0  
       currenty = 0  
       tilei = 0  
       while currenty < image_file.size[1]:  
         while currentx < image_file.size[0]:  
           print currentx,",",currenty  
           tileset.append( image_file.crop((currentx,currenty,currentx + BOX_SIZE, currenty + BOX_SIZE)) )  
           tilei += 1  
           currentx += BOX_SIZE  
         currenty += BOX_SIZE  
         currentx = 0  
     # get the background numbers and use to get the tiles    
     for i in range(len(background) ):  
       for j in range(len(background[0])):  
         image = ImageQt( tileset[ background[j][i] ] )  
         pixmap = QtGui.QPixmap.fromImage(image)  
         image = QtGui.QPixmap(pixmap)  
         label = QtGui.QLabel(self)  
         label.setGeometry(i*BOX_SIZE+10, j*BOX_SIZE+10, BOX_SIZE, BOX_SIZE)  
         label.setPixmap(image)  
 save()  
 app = QtGui.QApplication(sys.argv)  
 width = 320  
 height = 320  
 w = MyImage(None, width, height)  
 w.setGeometry(100, 100, width+20, height+20)  
 w.show()  
 app.exec_()  

Had my computer lying around...



Been away from here for some time, basically I haven't been doing much fun stuff lately that seemed important to share here. But, last week, a friend asked me if I could help him counting people heads in a store. Basically, there is this very expensive hardware that use two cameras side by side, with an IR only filter and an IR emitter, so the idea is to find what is a head in a image and at what distance this head is from the camera pair, that's supposed to be installed on the ceiling, if the heads are in some range of height, they are probably human, so the camera counts plus one.

Ok so he asked me: can we do it? I thought for a minute, and replied "Yes, but you will need to buy some hardware to test it, don't know if it's worth the time", because it would take some time to receive the product and only then start to prototyping... And then it hit me! My laptop has stereoscopic camera! Emailed him, "give me two hours". Short story, in two hours, knowing near nothing on video processing, I could put together this code - using also code from around the web - that could do something near what he needed.

Ok, this codes needs to calculate the distance of something to the camera, so the idea is simple, get whatever moves more in one image, get the same for the other camera, assume that it's the same thing, and use math to calculate the distance. My equation came from this article: http://photon07.pd.infn.it:5210/users/dazzi/Thesis_doctorate/Info/Chapter_6/Stereoscopy_(Mrovlje).pdf

Viewing angle was measured using my bedroom wall, the points at each top corners of the screen where marked on the wall, and distance between then was measured and also the distance from the wall to the computer, the angle came from the resulting triangle. Did this once for each camera.

 // First example on image processing  
 // Finds the square with bigger difference from the latter  
 // Do it for both cams  
 // Guess that it's in the same object  
 // Tell the distance from the object  
 // Prototype to be further rewritten in OpenCV  
 // This code uses snippets form this great german website:  
 // http://www.creativecoding.org/lesson/topics/video/video-in-processing  
 // Aktivität in Bildbereichen feststellen in Processing  
 import processing.video.*;  
 final int VIDEO_WIDTH = 320;  
 final int VIDEO_HEIGHT = 240;  
 final int VIDEO_COLS  = 16;  
 final int VIDEO_ROWS  = 12;  
 float[] activityR = new float[VIDEO_COLS * VIDEO_ROWS];  
 float[] buffer1R = new float[VIDEO_WIDTH * VIDEO_HEIGHT];  
 float[] buffer2R = new float[buffer1R.length];  
 float[] buffer3R = new float[buffer1R.length];  
 float[] activityL = new float[VIDEO_COLS * VIDEO_ROWS];  
 float[] buffer1L = new float[VIDEO_WIDTH * VIDEO_HEIGHT];  
 float[] buffer2L = new float[buffer1L.length];  
 float[] buffer3L = new float[buffer1L.length];  
 float Max=0;  
 int maxIndex;   
 Capture camR = null;  
 Capture camL = null;  
 int POSITIONXR = 0;  
 int POSITIONXL = 0;  
 float Distance = 0;  
 int maxIndexN(float[] array) {  
  float Max=0;  
  int maxIndex = 0;   
  for(int i = 0; i<array.length; i++){   
    if(array[i]> Max){   
     Max=array[i];   
     maxIndex = i;    
   }   
  }  
  return maxIndex;  
 }   
 void setup () {  
  size (640, 240);  
  camR = new Capture (this, VIDEO_WIDTH, VIDEO_HEIGHT,"LG 3D R Webcam", 30);  
  camL = new Capture (this, VIDEO_WIDTH, VIDEO_HEIGHT,"LG 3D L Webcam", 30);  
  frameRate (15);  
  camR.start();     
  camL.start();   
 }  
 void draw () {  
  if (camR.available ()) {  
   camR.read ();  
   int index;  
   int pxPerCol = VIDEO_WIDTH / VIDEO_COLS;  
   int pxPerRow = VIDEO_HEIGHT / VIDEO_ROWS;  
   image (camR, 0, 0);  
   for (int i=0; i < activityR.length; i++) {  
    activityR[i] = 0;  
   }  
   for (int i=0; i < camR.pixels.length; i++) {  
    //Calculates activity for the Right Camera   
    int x = (int) ((i % camR.width) / pxPerCol);  
    int y = (int) ((i / camR.width) / pxPerRow);  
    index = y * VIDEO_COLS + x;  
    color col = camR.pixels[i];  
    float sum = red (col) + green (col) + blue (col);  
    float deltaPixel = (buffer1R[i] + buffer2R[i] + buffer3R[i]) / 3 - sum;  
    if (deltaPixel < 0) {  
     deltaPixel *= -1;  
    }  
    activityR[index] += deltaPixel;  
    buffer3R[i] = buffer2R[i];  
    buffer2R[i] = buffer1R[i];  
    buffer1R[i] = sum;  
   }  
   int numeroQ = maxIndexN(activityR);  
   // This simply plots the X,Y position of the maxActivity index    
   textSize(32);  
   fill(255, 255, 255);  
   text(numeroQ%VIDEO_COLS, 10, 30);  
   text(",", 50, 30);  
   text(numeroQ/VIDEO_COLS, 60, 30);  
   POSITIONXR = numeroQ%VIDEO_COLS;  
   // Set activity for the right camera  
   for (int i=0; i < activityR.length; i++) {  
    activityR[i] /= pxPerCol* pxPerRow;  
    stroke (255, 20);  
    fill (0, 255, 230, activityR[i]);  
    rect ((i % VIDEO_COLS) * pxPerCol, (i / VIDEO_COLS) * pxPerRow, pxPerCol, pxPerRow);  
   }  
  }  
   if (camL.available ()) {  
   camL.read ();  
   int index;  
   int pxPerCol = VIDEO_WIDTH / VIDEO_COLS;  
   int pxPerRow = VIDEO_HEIGHT / VIDEO_ROWS;  
   image (camL, 320, 0);  
   for (int i=0; i < activityL.length; i++) {  
    activityL[i] = 0;  
   }  
   for (int i=0; i < camL.pixels.length; i++) {  
    // Calculate activity for the Left Camera  
    int x = (int) ((i % camL.width) / pxPerCol);  
    int y = (int) ((i / camL.width) / pxPerRow);  
    index = y * VIDEO_COLS + x;  
    color col = camL.pixels[i];  
    float sum = red (col) + green (col) + blue (col);  
    float deltaPixel = (buffer1L[i] + buffer2L[i] + buffer3L[i]) / 3 - sum;  
    if (deltaPixel < 0) {  
     deltaPixel *= -1;  
    }  
    activityL[index] += deltaPixel;  
    buffer3L[i] = buffer2L[i];  
    buffer2L[i] = buffer1L[i];  
    buffer1L[i] = sum;  
   }  
   int maxIndexN = maxIndexN(activityL);  
   // Just here to write the maxActivity X,Y position    
   textSize(32);  
   fill(255, 255, 255);  
   text(maxIndexN%VIDEO_COLS, 330, 30);  
   text(",", 370, 30);  
   text(maxIndexN/VIDEO_COLS, 380, 30);  
   POSITIONXL = maxIndexN%VIDEO_COLS;  
   for (int i=0; i < activityL.length; i++) {  
    //Just to ´rint out the activity  
    activityL[i] /= pxPerCol* pxPerRow;  
    stroke (255, 20);  
    fill (0, 255, 230, activityL[i]);  
    rect ((i % VIDEO_COLS) * pxPerCol+320, (i / VIDEO_COLS) * pxPerRow, pxPerCol, pxPerRow);  
   }  
   // Calculates distance, uses 50° as the viewing angle  
   Distance = 35*VIDEO_COLS / (2*tan(0.436)*(POSITIONXL-POSITIONXR));  
   // Prints out the calculated distance  
   textSize(32);  
   fill(255, 255, 255);  
   text(Distance, 200, 200);  
   text("DISTANCE =", 10, 200);   
  }  
 }  
 // Conclusion here is that the most hard is how to tell that whatever you select on left camera to find  
 // the distance from camera, what's the same thing on the right camera.
 //
 // Copyright 2014 Érico Porto
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
 //
 //    http://www.apache.org/licenses/LICENSE-2.0
 //
 // Unless required by applicable law or agreed to in writing, software
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
   

Powered by Blogger.