Archive for the ‘Games’ Category

Drawing Gradients with PyGame

Saturday, November 20th, 2010

I have been learning myself some PyGame on my free time. Best way to learn new technologies, I believe, is to develop small project and doing a lot of reading. Everybody seems to agree on the former but most fail to put effort doing the latter.

I needed to draw gradients for the background of my little learning project. First I tried the naive approach: iterating over each pixel and setting the correct value. This takes forever as you could have guessed. I continued my research to find out the close connection between PyGame and NumPy. Yes, fast array operations. My gradient generating code using linspace and tile is below:

def generate_gradient(fromcolor, tocolor, height, width):
    channels = []
    for channel in range(3):
        fromvalue, tovalue = fromcolor[channel], tocolor[channel]
        channels.append(numpy.tile(numpy.linspace(fromvalue, tovalue, width),
                                                                  [height, 1]))
    return numpy.dstack(channels)

I create three 1D arrays for each channel and then tile them to get a 2D array. Then I stack those 2D arrays and get combined (r, g, b) values. Each 1D array contain values linearly sampled between given maximum and minimum values by linspace. I can then blip this array on a surface like this:

gradient = generate_gradient((63, 95, 127), (195, 195, 255), 1024, 600)
pygame.surfarray.blit_array(some_surface, pygame.surfarray.map_array(some_surface, gradient))

This works quite well. But I was curious whether there is a better method, so I did some googling. First I stumbled upon James Tauber’s gradient code. Arrays! I was a bit ashamed not remembering standard library had array module. James Tauber’s code builds an array with the correct values and then writes the image as a PNG file. It also has the ability to generate multi-gradients.

Then I found gradients package for PyGame. This package does much more than linear gradients. It creates a PyGame surface of 1 pixel thickness, fills it with the gradient values and then resizes it.

I made a small test to see which method runs faster. I had to strip file and compression related code from James Tauber’s gradient module. I run a simple method tha just generates a gradient image/array in memory and returns it 10 time, below is the average times:

  • 320×240:
    • numpy: 0.01469659805
    • array: 3.6310561180
    • pygame: 0.0069756984711
  • 800×600:
    • numpy: 0.1323119163
    • array: 19.9719331265
    • pygame: 0.0192141056061

Here numpy is my method above, array is the stripped version of James Tauber’s code and pygame is the gradiends package. If my benchmarking method doesn’t have dramatic errors fastest way to generate linear gradients is to fill a strip with correct values and then to resize it.

Now I’ll go back to work on my learning project.

Bookmark and Share

Using Layouts In Qooxdoo – Part 3: HBox Layout

Sunday, February 15th, 2009

This is the third part of a tutorial series about layout managers, container objects and object hierarchies in Qooxdoo. Read the first part here.

qx.ui.layout.HBox layout manager is similar to VBox but it places its children side by side.

HBox Layout

HBox Layout

Let us begin with a simple application:

   1 /*
   2 #asset(hbox/*)
   3 */
   4 qx.Class.define("hbox.Application",
   5 {
   6     extend: qx.application.Standalone,
   7     members: {
   8         main: function(){
   9             this.base(arguments);
  10 
  11             var main_container = new qx.ui.container.Composite();
  12 
  13             var layout_manager = new qx.ui.layout.HBox();
  14             main_container.setLayout(layout_manager);
  15 
  16             main_container.add(new qx.ui.form.Button("Child Widget 1"));
  17             main_container.add(new qx.ui.form.Button("Child Widget 2"));
  18             main_container.add(new qx.ui.form.Button("Child Widget 3"));
  19 
  20             var application_root = this.getRoot();
  21             application_root.add(main_container);
  22         }
  23     }
  24 });
HBox Example 1

HBox Example 1

Ok, nothing special here. In the next example we will see how we can apply different constraints to individual child elements:

   1 /*
   2 #asset(hbox/*)
   3 */
   4 qx.Class.define("hbox.Application",
   5 {
   6     extend: qx.application.Standalone,
   7     members: {
   8         main: function(){
   9             this.base(arguments);
  10 
  11             var main_container = new qx.ui.container.Composite();
  12 
  13             var layout_manager = new qx.ui.layout.HBox();
  14             main_container.setLayout(layout_manager);
  15 
  16             main_container.add(new qx.ui.form.Button(
  17                                            "Child Widget 1").set({width:300}));
  18             main_container.add(new qx.ui.form.Button("Child Widget 2"));
  19             main_container.add(new qx.ui.form.Button(
  20                                             "Child Widget 3").set({width:75}));
  21             main_container.add(new qx.ui.form.Button(
  22                                 "Child Widget 4 has a slightly longer title"));
  23 
  24             var application_root = this.getRoot();
  25             application_root.add(main_container);
  26         }
  27     }
  28 });
HBox Example 2

HBox Example 2

We have set some properties on child widgets here. I know, it’s a bit ugly doing it on the same line with the addition. But screen space is more expensive now on the intertubes since the economic crisis1. The important point here is we have set width of first and third widgets explicitly. Second and fourth will be automatically sized. Also notice that the fourth widget is wider. Buttons consider length of their labels when calculating their sizes.

The container will also be auto-sized on creation. But it won’t fill the viewport since we haven’t coded this behaviour (see previous part). Next example will include this behaviour:

   1 /*
   2 #asset(hbox/*)
   3 */
   4 qx.Class.define("hbox.Application",
   5 {
   6     extend: qx.application.Standalone,
   7     members: {
   8         main: function(){
   9             this.base(arguments);
  10 
  11             var main_container = new qx.ui.container.Composite();
  12 
  13             var layout_manager = new qx.ui.layout.HBox();
  14             main_container.setLayout(layout_manager);
  15 
  16             main_container.add(new qx.ui.form.Button("Child Widget 1").set({
  17                 width: 300,
  18                 allowGrowX: true,
  19             }), {flex: 1});
  20             main_container.add(new qx.ui.form.Button("Child Widget 2").set({
  21                 minWidth: 75,
  22                 maxWidth: 200,
  23             }), {flex: 1});
  24             main_container.add(new qx.ui.form.Button("Child Widget 3").set({
  25                 width: 75,
  26                 allowGrowX: false,
  27             }), {flex: 1});
  28 
  29             var application_root = this.getRoot();
  30             application_root.add(main_container);
  31 
  32             application_root.addListener("resize", function(e) {
  33                 main_container.set({
  34                     "width": qx.bom.Viewport.getWidth(),
  35                 });
  36             }, this);
  37         }
  38     }
  39 });
HBox Example 3

HBox Example 3

We have quite a few changes this time. First of all each widget is set to flex. First and third widgets have their (different) widths explicitly set. Growing is enabled for the first2 and disabled for the third. For the second widget we have created different constraints; we have set the maximum and minimum values for width.

Try resizing the window to see how each widget reacts. We should note that the second widget never goes beyond its limits. While the other two conform to fit the containers space. MinWidth and MaxWidth are stronger constraints than width.

Let’s try constraining the container’s size:

   1 /*
   2 #asset(hbox/ *)
   3 */
   4 qx.Class.define("hbox.Application",
   5 {
   6     extend: qx.application.Standalone,
   7     members: {
   8         main: function(){
   9             this.base(arguments);
  10 
  11             var main_container = new qx.ui.container.Composite();
  12 
  13             var layout_manager = new qx.ui.layout.HBox();
  14             main_container.setLayout(layout_manager);
  15             main_container.set({
  16                 maxWidth: 900,
  17                 minWidth: 400,
  18             });
  19 
  20             main_container.add(new qx.ui.form.Button("Child Widget 1").set({
  21                 width: 300,
  22                 allowGrowX: true,
  23             }), {flex: 1});
  24             main_container.add(new qx.ui.form.Button("Child Widget 2").set({
  25                 minWidth: 75,
  26                 maxWidth: 200,
  27             }), {flex: 1});
  28             main_container.add(new qx.ui.form.Button("Child Widget 3").set({
  29                 width: 75,
  30                 allowGrowX: false,
  31             }), {flex: 1});
  32 
  33             var application_root = this.getRoot();
  34             application_root.add(main_container);
  35 
  36             application_root.addListener("resize", function(e) {
  37                 main_container.set({
  38                     "width": qx.bom.Viewport.getWidth(),
  39                 });
  40             }, this);
  41         }
  42     }
  43 });

We have set the maximum and minimum width for the container widget. If you resize the window you should notice the container obeying these constraints.

VBox & HBox Wrap-up

VBox and HBox are simple layout managers. I have mostly tried to illustrate how container preferences, layout preferences and child widget preferences work together with examples. In the next chapters we’ll work on more advanced layout managers and then composite layouts with multiple containers.

NEXT PART: Grid Layout


1: This is a lame joke, I’ve just done that for the sake of brevity.

2: This is redundant. Grow and Shrink properties are true by default.

Bookmark and Share

Feed URL

Monday, January 19th, 2009

Appereantly Google has acquired FeedBurner and planning to do some changes with it. So I’d like to remove that external dependency and stop using FeedBurner. Please check your feed URL:

  • If you are using http://www.muhuk.com/feed/ then all is fine.
  • If your feed URL is http://feeds.feedburner.com/muhuk please change it to http://www.muhuk.com/feed/
Bookmark and Share

Kongai

Friday, July 18th, 2008

Kongregate‘s collectable card game Kongai is online. For those who don’t know about Kongregate; it is a flash game portal with social networking with some game elements. Games are (well, some of them) connected to Kongregate through its API and then authenticated users level up (in Kongregate) and win badges for achievements. So I think we can say Kongregate is a game of some sort.

I played Kongai a couple of times. It looks really nice, obviously more time spent on visuals than average flash game. Game is simple. I mean elementary school kid simple. You don’t even need to do basic math most of the time. And the AI is quite stupid. When I have a long range only card and the bot has a short range only card, it just doesn’t occur her to step closer and she keeps getting hit. But, I guess they can easily fix that.

The major flaw of the game, for me, is when you are playing against the computer (the bot) it runs on the server. So you wait. And you wait a lot, something like 8 seconds every turn on my computer (compare this with your action time; <0.1 secs). Even if the bot becomes more intelligent (and fast) this is a big turn off. When you are playing with someone else you will have to double the waiting time and double the discomfort.

But this is all not very important for me. Becuse the game is too simple to be enjoyable anyway. I have been expecting something more like Castlewars. Think about it a collectible Castlewars with a wide selection of possible cards. That would be something I can come back to play again and again.

By the way, there is one game I came back to play every now and then. You know these flash games are all casual games. But this one is so beautifully designed (and executed) you just don’t lose interest so soon. I recommend everyone who likes games to try Gemcraft, I guarantee you money back that you will like it. :)

Bookmark and Share