Isometric Views

16 views
Skip to first unread message

stabbin...@gmail.com

unread,
Apr 4, 2010, 2:11:01 AM4/4/10
to Gumm's RPG Cloud
I did some searching. Virtually all the isometric Pygame libraries I
could find were crude, buggy, unfinished, or unappealing.

The only one that has some appeal is PGU, but I couldn't easily see a
way to integrate it with OpenGL-2D imaging for the acceleration--which
I think we're really going to need, especially with your desire to do
particles and effects.

Here's all the stuff I found:
* pyTyle - http://www.pygame.org/project/871/
* PGU - http://www.pygame.org/project-PGU+-+Phil%27s+pyGame+Utilities-108-.html
* Isotope - http://www.pygame.org/project-Isotope-167-.html
* PygLibs - http://www.pygame.org/project/283/
* Isomyr - http://www.pygame.org/project-Isomyr-1316-.html
* pyEmpire (game) - http://www.pygame.org/project-pyEmpires-1193-.html
* lwpgt - http://www.pygame.org/project-light+weight+python+game+tools-1301-.html

So I did some study. Math, can ya believe it? I suck at math. :) But I
learned some basics, and put together a program to demonstrate tricks
that I think could be useful for our goals.

Uploaded here. http://groups.google.com/group/gumms-rpg-cloud/web/simple_isometric_demo.py?hl=en

I designed this so I could think and code in 2D, and have the 2D space
translated to an isometric space in screen coordinates. It uses
primitive geometry to show a spacial grid and a few moving dots for
visual orientation. With some work it might be grown to accommodate
isometric pixel art. It is graphics system-agnostic, so we could use
SDL or OpenGL to render the images. And there are some pretty good
OpenGL-2D libraries out there.

Unfortunately, using the dynamic rotation and tilt aspect of the demo
in a real game would require real 3D imaging so it is out of the
question. Realistically we would probably pick a static angle (45
degrees) and a static tilt (0.6) and then fashion all our pixel art to
fit that geometry.

Give it a look and let me know what you think.

Gumm

stabbin...@gmail.com

unread,
Apr 4, 2010, 2:49:43 PM4/4/10
to Gumm's RPG Cloud
I think you'll really like this one.

simple_isometric_demo_2.py: http://groups.google.com/group/gumms-rpg-cloud/web/simple_isometric_demo_2.py

This demo adds:
* Reverse translation for screen points (isometric view) to 2D (game
geometry) for things like translating mouse coordinates, screen edges,
etc.
* Intuitive panning via mouse clicks.
* Functions for performing trig calculations: angle given two points;
point given an origin, radius, and angle.

Gumm

stabbin...@gmail.com

unread,
Apr 4, 2010, 3:03:36 PM4/4/10
to Gumm's RPG Cloud
Oh. And think of each isometric cell as a tile. In the demo there are
only four tiles, and the avatar can "move" anywhere. In a real
implementation each cell would have a isometric pixel-art tile to be
rendered, and may or may not represent an obstacle to the avatar.

What remains:
* Integrate with CMV.
* What tiles look like.
* How tiles are loaded.
* Which tiles get drawn to the screen. Maybe all, and allow SDL to
clip those out of bounds? If I recall, Gummworld didn't sacrifice FPS
by doing this.
* Rendering library: Pygame + OpenGL? The good libs behave much like
Pygame + SDL as far as loading and drawing images.
* Things like collision detection to enforce avatar honoring map
bounds and obstacles.
* Going crazy: story, GUI, anim, combat and GFX, SFX.

Gumm

Jonathan Hollocombe

unread,
Apr 5, 2010, 5:32:49 AM4/5/10
to gumms-r...@googlegroups.com
From a brief glance this looks good. I'll take a proper look over the next week.

Good work :)

Jon.

stabbin...@gmail.com

unread,
Apr 5, 2010, 10:40:58 AM4/5/10
to Gumm's RPG Cloud
The latest, isometric_demo_3: http://gumms-rpg-cloud.googlegroups.com/web/isometric_demo_3.zip

I moved the view class into a module and made 25x25 tiles. Up until
20x20 I was getting 60 fps. When I upped it to 25 I took a performance
hit, only 40 fps. That suggests we would have to do some kind of
zoning, e.g. only process tiles in the vicinity of the avatar. I did
the same thing for Gummworld and it boosted performance even on
humongous tile sets.

I left demo1 and demo2 files up just in case you wanted to see the
progression to demo3. Sometimes it helps to see the baby steps.

Experimentally, I tried pymike's pygl2d in demo4. Performance was a
lot worse and I don't know why. Also, the circle primitive was off: it
did not draw in the correct position on the screen. I'll take a look
at the others and see if any are usable "out of the box".

Gumm

Jonathan Hollocombe

unread,
Apr 7, 2010, 5:41:56 PM4/7/10
to gumms-r...@googlegroups.com
I've had a play with this... nice little demo app.

My immediate comment would be that I can see 2 obvious optimisations:
1. You are currently transforming the tiles from 2d to isometric
coords on each draw, I think they only need to be calculated when the
view angle changes. The new coords could be stored in the Tile2d
objects and updated only if the angle changes.
2. You are currently flipping the entire screen even though only a
tiny amount of it has updated. I understand that this is only a simple
demo so getting the dirty rects for everything would be overkill but
this would speed up the fps.

I've had a try at adding images to the tiles, it needs to be cleaned
up but it works (sort of). See attached file for result.

I also trying running the isometric_view.py with psyco. This gave me
an increase from 46-47 fps to 60-61 fps. I think this speed increase
is due to the isometric transformations which psyco would speed up
alot. I'm not suggesting using psyco, just an interesting experiment.

Cheers.

isometric_demo_3_new.zip

B W

unread,
Apr 8, 2010, 1:53:28 AM4/8/10
to gumms-r...@googlegroups.com
I like what you did, Jon. It was very cool to see the real tiles. :)

I spent a couple hours tonight and made some optimizations inspired by yours, and a few of my own. See attached.

Depending on the frames-per-second we want to get, it looks like we might get about 1000 tiles in a zone (zone=active tiles; not necessarily tiles on-screen) with SDL. Zoning would probably be part of the tile treadmill, which we've yet to design. I ran 32x32 at 55+ FPS.

The changes I made...
* IsometricView constructor arguments now takes screen instead of screen center. The body of other methods were adjusted to use the derived screen center.
* Tile2D uses a pre-drawn isometric tile. It's plain, like the trapezoid primitive, but I really just wanted to see the isometric grid. See comments for why I think pre-draw isometric tiles are the way we probably should go. But it sounds like you've been thinking along other lines, like maybe we should let the player rotate the isometric view? FIFE allows the user to rotate the view by 90 degrees, and it requires you to use Blender to render every image from all four angles.
* Tile2D and IsometricTiles use two rects: one to keep track of 2D space and one to support efficient isometric translation (by simply computing the rotated center).

I tried doing like you suggested, saving the geometry of the translated tiles. I can't figure out a way to do it. The reason is because the game moves internally in 2D space, and the isometric view moves in translated space. So if the camera pans upward the screen pans up-right. Maybe we could cheat and pan the screen diagonally (equivalent to 45 degrees) but that would limit the isometric view. The alternative of translating the image center is pretty inexpensive.

I did the screen flipping because of my experience with Gummworld, my first attempt at a tile scroller. Bascially if you're scrolling the playfield you're updating the whole screen. I actually saw frame rates go down drastically when I tried dirty updates. Dirty updates might pay off if the scrolling playfield is a partial area of the screen. For example, GUI and stats panels may be relatively static.

I love psyco. Unfortunately, the build I downloaded for Python 2.6 a while back behaved badly with Trolls Outta Luckland so I kind of gave up on it. It was a sad day. I've noticed psyco speeds up things like bytecode-heavy loops really well, and even some parts of Pygame.

bw
isometric_demo_3_2.zip

B W

unread,
Apr 9, 2010, 1:49:24 AM4/9/10
to gumms-r...@googlegroups.com
Hi, Jon.

Attached is an OpenGL version.

This is my OpenGL environment:
  • PyOpenGL 3.0.1b2 (I did *not* install the OpenGL-accelerate bundle)
  • Rabbyt-0.8.3
  • PIL-1.1.7 (needed for many OpenGL demos, I think)
  • numpy-1.4.0 (needed for many OpenGL demos, I think)
My first attempt was with pygl2d. That one was terribly slow so I gave up on it.

Gloss provides a Game class that you have to subclass, and override methods to update, draw, etc. It was too restrictive to fit into our CMV design. However, it has a particles class which looks promising. Something to consider.

OpenGLLibrary is a bit too advanced for me right now. The demos are very slick, though.

There is a Pygame cookbook recipe I got working a while back, as well. It's not maintained, though. It's more of an example. http://www.pygame.org/wiki/SimpleOpenGL2dClasses?parent=CookBook

Rabbyt was not hard to integrate. It is fairly well documented.The performance is great, as you'll see. The Sprite class is incredibly easy to use for rotation and scaling, and as we learn more we can get as fancy with OpenGL as we like. I had to do some voodoo with the viewport (screen) and texture (tile image) to get the screen coordinates and tile appearance oriented like Pygame--that was a bit challenging. The voodoo code is documented in the source and easy to reuse. The one major drawback is no one seems to know how to invoke transparency, a la Pygame Surface.set_colorkey. I had to use Gimp to add a transparency layer to the images. We will have to do this for all images unless we can figure out the trick. I'd be surprised if OpenGL didn't provide a means.

Have fun.

Gumm
isometric_demo_4.zip

Jonathan Hollocombe

unread,
Apr 10, 2010, 12:40:11 PM4/10/10
to gumms-r...@googlegroups.com
Hi,

Managed to get this working and it looks good. Are you using 2.6 for
this? I couldn't find a download for Rabbyt for 2.5.

I've made some images for a test player (just a 3d arrow) using a
script I found for Blender that produces isometric images from 3d
models. I've knocked together a quick player class to load and use
these images. I think the result has potential. Let me know what you
think.

Cheers.

p.s. If we go much further with this demo we should think about an
on-line repository for it, the zip file is getting bigger and we don't
want to lose any change history.

isometric_demo.zip

stabbin...@gmail.com

unread,
Apr 10, 2010, 12:52:57 PM4/10/10
to Gumm's RPG Cloud
Just an observation.

I think we are going to have to stick with Rabbyt or similar OpenGL
library. The frame rate of the SDL version (isometric_demo_3_2.py) at
larger screen sizes is pretty bad. I get about 35 fps on a dual-core
computer at 800x600. The OpenGL version gets 55-60 fps on my crappy
laptop's integrated ATI graphics adapter. Even at 1280x800 I'm getting
near 60 fps.

Gumm

stabbin...@gmail.com

unread,
Apr 10, 2010, 12:59:33 PM4/10/10
to Gumm's RPG Cloud

On Apr 10, 9:40 am, Jonathan Hollocombe <jholloco...@googlemail.com>
wrote:


> Hi,
>
> Managed to get this working and it looks good. Are you using 2.6 for
> this? I couldn't find a download for Rabbyt for 2.5.

Yes, I haven't seen any red flags for 2.6 so that is what I've been
using.

> I've made some images for a test player (just a 3d arrow) using a
> script I found for Blender that produces isometric images from 3d
> models. I've knocked together a quick player class to load and use
> these images. I think the result has potential. Let me know what you
> think.

Oo, an advancement. I'll check it out. :)

> p.s. If we go much further with this demo we should think about an
> on-line repository for it, the zip file is getting bigger and we don't
> want to lose any change history.

I was thinking the same thing. It seems like we're almost out of proof-
of-concept mode. We should choose a source baseline and a code tree
soon.

Gumm

B W

unread,
Apr 10, 2010, 2:45:12 PM4/10/10
to Gumm's RPG Cloud
I love the arrows. They are awesome. :)

Drop the attached isometric_demo_5.py in your project directory and run it to see your art in action.

About the source...

There is a new Arrow class representing an arrow sprite. It contains a list of images, and swaps these out depending on the panning direction. It's the same idea used in Pygame sprites, but with Rabbyt calls. Lines 130-133 do the calculations to update the sprite image.

I don't know if Rabbyt was designed to be used this way, but it works and I don't know enough about OpenGL and Rabbyt yet to mess with the internals. The trick of modifying the sprite's texture attribute was pretty obvious, though. Besides, the loader callback is not documented in the API so it could change in any new release and break our stuff later.

Gumm
isometric_demo_5.py

B W

unread,
Apr 10, 2010, 2:59:18 PM4/10/10
to Gumm's RPG Cloud
On Sat, Apr 10, 2010 at 11:45 AM, B W <stabbin...@gmail.com> wrote:

Drop the attached isometric_demo_5.py in your project directory and run it to see your art in action.

Oops. I just realized you intended to have the arrow be the avatar. Add this code at line 147:

        self.arrow.xy = self.to_isometric((self.camera_pos,))[0]

Gumm

Jonathan Hollocombe

unread,
Apr 10, 2010, 4:28:50 PM4/10/10
to gumms-r...@googlegroups.com
Much tidier :)

However, there is one thing to notice... you've changed my hard coded
image names to loading the files as they are returned by os.listdir.
This approach may not work if there is more than 100 files as it will
probably load file1, file100, file2, file3, etc.

I can't imagine we'd have > 100 images per sprite but it's worth noting.

Interesting to note your use of rabbyt.Sprite.texture. I had a quick
look for a rabbyt texture loader but I didn't see one, didn't think of
using the Sprite class to do it.

Cheers.

B W

unread,
Apr 10, 2010, 6:17:14 PM4/10/10
to gumms-r...@googlegroups.com
On Sat, Apr 10, 2010 at 1:28 PM, Jonathan Hollocombe <jholl...@googlemail.com> wrote:
Much tidier :)

Yeah, I got on a roll. I thought I was done, but I saw a lot of slop while rereading demo 5. It was nagging me to fix it. I think I'm satisfied with the attached demo 6 now. At least, this will be my last modification today.

However, there is one thing to notice... you've changed my hard coded
image names to loading the files as they are returned by os.listdir.
This approach may not work if there is more than 100 files as it will
probably load file1, file100, file2, file3, etc.

I can't imagine we'd have > 100 images per sprite but it's worth noting.

You are right. I saw I could leverage the file names for the demo, but one shouldn't do that in a real program. It is best to explicitly name the resources and their order to avoid subtle errors like missing files. Better to have the loader complain than have it blow up much later and possibly lose game progress.

Interesting to note your use of rabbyt.Sprite.texture. I had a quick
look for a rabbyt texture loader but I didn't see one, didn't think of
using the Sprite class to do it.


Ha. I was able to figure it out. :) I noticed late that Rabbyt's main docs page documents module routines. I totally overlooked those as I dove straight for the Sprite links. The recommended method is rabbyt.auto_detect_load_texture(). It wasn't that simple, though! I had to dig into the rabbyt.Sprite class to see how it was initializing sprites. In addition to setting sprite.texture_id, you have to set sprite.shape to fit the texture dimensions. The new Arrow._load_texture() does this.

I don't see any garbage collection for textures. We may need to come up with a management class to easily free up resources. If you delete a sprite I'm not sure the texture goes away.

We may need to figure out OpenGL fonts, too. The Rabbyt source distribution has a Font class in the Pygame font demo. But it uses Pygame's Surface.tostring() to convert the font to an OpenGL texture, which I've read is very slow. It may be okay if you pre-render your textures during level loading, but you don't want to do a lot of it in real-time.

Gumm
isometric_demo_6.py

Jonathan Hollocombe

unread,
Apr 11, 2010, 1:35:37 PM4/11/10
to gumms-r...@googlegroups.com
> You are right. I saw I could leverage the file names for the demo, but one
> shouldn't do that in a real program. It is best to explicitly name the
> resources and their order to avoid subtle errors like missing files. Better
> to have the loader complain than have it blow up much later and possibly
> lose game progress.

As a side note I've tried this code in Linux and it doesn't work:
os.listdir does not return a sorted list in Linux so you have to use
sorted(os.listdir). Interesting to know.

I've gone through and re-factored the code to use the MVC pattern.
It's not optimum and there's definitely room for improvement but it
shows how it can be done. I've added another view (a simple debug
console output if you run the game with command line option: -d) to
show how easily it can be extended. I haven't really extracted the
"model" out of the code so it's more just "views" and "controllers" at
the moment, this is because the IsometricView class currently contains
both "model" and "view" elements so would need to be reworked to
separate them out.

Let me know what you think. (I haven't included any of the images in
the zip, they are unchanged).

Cheers.

isometric_demo_MVC.zip

B W

unread,
Apr 13, 2010, 12:03:14 AM4/13/10
to gumms-r...@googlegroups.com
On Sun, Apr 11, 2010 at 10:35 AM, Jonathan Hollocombe <jholl...@googlemail.com> wrote:

os.listdir does not return a sorted list in Linux so you have to use
sorted(os.listdir). Interesting to know.

Ah. Python must use an unassisted readdir() in accordance with POSIX. Winders is not POSIX compliant. :P I just replaced it with a loop to generate the filenames.
 
I've gone through and re-factored the code to use the MVC pattern.
It's not optimum and there's definitely room for improvement but it
shows how it can be done. I've added another view (a simple debug
console output if you run the game with command line option: -d) to
show how easily it can be extended.

I like your file organization better than mine. I ended up with way too many files, hard to manage. You did a pretty good job separating.

However, I'm finding MVC in general hard to follow. You have to know the message originator-subscriber relationships well, and it's hard to track down who is acting on the messages. I found it exceptionally hard to follow the string-based messages. The class-based messages were significantly easier (using grep, awk, perl, etc.) but still what I would call hard.
 
I haven't really extracted the
"model" out of the code so it's more just "views" and "controllers" at
the moment, this is because the IsometricView class currently contains
both "model" and "view" elements so would need to be reworked to
separate them out.

I took a first whack at separating them. I admit I'm having difficulty deciding what belongs to model versus view. I think my struggle is that I'm trying to do everything in the classes, and finding that they need to know about each other--which makes them interdependent when we need them to be independent.

I suspect we'll need to make the model and view more pure, and move manipulative code to new "glue" functions and classes to integrate the parts of MVC. See the attached for my first step in that direction: mouse_2d_to_model() and mouse_iso_to_model(). Let me know if you agree with this approach. Note that I'm pursuing this evolution just to work out the separation, with the intent that the parts can be easily plugged into MVC or whatever.

Let me know what you think. (I haven't included any of the images in
the zip, they are unchanged).

It's a good demonstration. And I see you were wrestling with some of the same things I was.

While I found tracing the string-based events hard, and I expect they are highly prone typographical error, I like how your implementation mimics the Pygame event class with event.type. It makes the cases in recieve() very programmer-friendly.

Gumm
new_7.py

B W

unread,
Apr 13, 2010, 12:14:52 AM4/13/10
to gumms-r...@googlegroups.com
Er, and you'll need utils.py.

Gumm
utils.py

Jonathan Hollocombe

unread,
Apr 13, 2010, 8:46:08 AM4/13/10
to gumms-r...@googlegroups.com
> However, I'm finding MVC in general hard to follow. You have to know the
> message originator-subscriber relationships well, and it's hard to track
> down who is acting on the messages. I found it exceptionally hard to follow
> the string-based messages. The class-based messages were significantly
> easier (using grep, awk, perl, etc.) but still what I would call hard.

I think the idea is that you don't need to know the
originator-subscriber relationships at all! If it has been implemented
properly the subscriber to an event should not care where the event
originated from, simply that it is interested in it. For example a
view should just sit there and pick up "model_changed" events,
updating themselves whenever this is seen. In theory, the view should
be coded in such a way that it doesn't matter why or who sent these
events.

I agree the string-based messages are brittle but don't agree you need
to replace them with a class hierarchy based approach. Another way to
do this is to use constants, e.g. events.MODEL_CHANGED, which could be
an int, string or whatever. This is the way it is done in pygame.

> I took a first whack at separating them. I admit I'm having difficulty
> deciding what belongs to model versus view. I think my struggle is that I'm
> trying to do everything in the classes, and finding that they need to know
> about each other--which makes them interdependent when we need them to be
> independent.
>
> I suspect we'll need to make the model and view more pure, and move
> manipulative code to new "glue" functions and classes to integrate the parts
> of MVC. See the attached for my first step in that direction:
> mouse_2d_to_model() and mouse_iso_to_model(). Let me know if you agree with
> this approach. Note that I'm pursuing this evolution just to work out the
> separation, with the intent that the parts can be easily plugged into MVC or
> whatever.

I've been having a go at creating a model / view separation. The way
i'm going is to have a 3D model (so i can implement jumping, etc.) and
a 2D isometric view. I think i've been getting stuck on similar areas
to you (mapping of mouse position from screen -> isometric view ->
model and vice-versa). Your functions look useful, i might try using
them in what i have so far.

> While I found tracing the string-based events hard, and I expect they are
> highly prone typographical error, I like how your implementation mimics the
> Pygame event class with event.type. It makes the cases in recieve() very
> programmer-friendly.

Duck typing, *quack quack*. That's why i love Python :)

Cheers, Jon.

B W

unread,
Apr 14, 2010, 10:14:35 AM4/14/10
to gumms-r...@googlegroups.com
Here's a version that has a fully separated model and view. It requires isometric_view.py, utils.py, and the arrow images.

Basically I moved all the update() and draw() methods out of the classes because they required knowledge of the other half. They are now in functions that "glue" the model and view together, which I called Implementation. That was the easiest and obvious next step. This change, and the change in the top-down view from Pygame surfaces/sprites to Rabbyt allowed me to change the view on the fly (use the "v" key).

Next I'll see if I can move some of the code back into class members to make the update and draw functions clearer.

Gumm
new_8.py

Jonathan Hollocombe

unread,
Apr 14, 2010, 10:39:39 AM4/14/10
to gumms-r...@googlegroups.com
Hi, looks good.

Had a quick glance through and noticed your comment about the fact
that the model needs to interact with the view in the update_avatar
function.

In this function the view is only referenced in the code: avatar.angle
= angle + gv.angle. I'm not sure exactly what this is doing but it
strikes me that the model shouldn't need to know about what angle the
avatar is at in the view? I think it must be possible for the avatar
to only be aware of its angle in the model coords and for the view to
transform this angle to Iso/Topdown coords for rendering.

I'm working on code which is duplicating a lot of what you have done,
but in a slightly different way so i think it's worth comparing the
two solutions. I'm producing this a lot slower than you though, so it
may be some time before i can post anything :)

Cheers, jon.

B W

unread,
Apr 15, 2010, 12:33:34 AM4/15/10
to gumms-r...@googlegroups.com
On Wed, Apr 14, 2010 at 7:39 AM, Jonathan Hollocombe <jholl...@googlemail.com> wrote:
In this function the view is only referenced in the code: avatar.angle
= angle + gv.angle. I'm not sure exactly what this is doing but it
strikes me that the model shouldn't need to know about what angle the
avatar is at in the view? I think it must be possible for the avatar
to only be aware of its angle in the model coords and for the view to
transform this angle to Iso/Topdown coords for rendering.

Done.
 
I'm working on code which is duplicating a lot of what you have done,
but in a slightly different way so i think it's worth comparing the
two solutions. I'm producing this a lot slower than you though, so it
may be some time before i can post anything :)

I included the complete collection except for the arrow images. I guess you couldn't run version 8 because you were missing the new top-down images. I think this version completely separates model and view. Should be easier to integrate with MVC.

Also, I could not see any more chunks of code to move into the model or view classes. However, it may be easier to do that in MVC because you can fire messages rather than de-reference objects to make the updates occur.

Gumm
new_9.zip

B W

unread,
Apr 15, 2010, 12:48:59 AM4/15/10
to gumms-r...@googlegroups.com
Quick note. Found a bug in new_9.py. :)

Line 125: I neglected to change the stats display to accommodate the new way the avatar classes' angles update.

Change
gm.avatar.angle + gv.angle
to
gm.avatar.angle

And if you're a neat freak like me, you don't need to pass gv to Clock.tick() anymore.

Gumm

Jonathan Hollocombe

unread,
Apr 16, 2010, 2:26:35 PM4/16/10
to gumms-r...@googlegroups.com
Finally...

OK, so in order to get my head around the maths involved I've pretty
much gone back to the start. Much head-scratching and graph-doodling
later i've come up with something that works, and i'm pretty happy
with the result. Let me know what you think, and if you have any
questions about it.

jon.
--
Subscription settings: http://groups.google.com/group/gumms-rpg-cloud/subscribe?hl=en
iso_demo.py
isometric.py

Jonathan Hollocombe

unread,
Apr 16, 2010, 4:33:03 PM4/16/10
to gumms-r...@googlegroups.com
I've created another quick demo of my 3D -> isometric transform, this
time it's a jumping arrow.
iso_demo2.py
models.py

B W

unread,
Apr 17, 2010, 3:47:15 PM4/17/10
to gumms-r...@googlegroups.com
This is interesting, Jon. But I'm finding it extremely challenging.

I'm having trouble figuring out how to manipulate the model. I was sort of able to get tiles displayed with problems that are likely treatable. The biggest issue I have is I can't get panning to work, and the framerate is below 20.

So I went back to study the model, and something weird is happening. Add the following lines to the main loop, before model.update(None):

    x,y,z = model.player_pos
    x += 1
    model.player_pos[:] = x,y,z

I expected this to move the player (arrow) in the positive direction along the x-axis, but it doesn't. I did successfully make the arrow bob by varying the z-axis, so I don't understand why I'm failing with x. Maybe you could help me understand?

Gumm

B W

unread,
Apr 17, 2010, 3:49:08 PM4/17/10
to gumms-r...@googlegroups.com
Actually, I remember now that I was varying the z-axis of the Arrow sprite, not the model. Varying the model doesn't do what I want. :)

Gumm

Jonathan Hollocombe

unread,
Apr 17, 2010, 4:05:40 PM4/17/10
to gumms-r...@googlegroups.com
Hi Gumm,

This is a "feature" of the model: it attempts to move the player to
the model.target which is fighting your attempts to move the player
manually. I haven't actually tested the target tracking yet, was
concentrating on get the jumping working.

Comment out the block in models.py:
v = self.target - self.player_pos
v = v * 1/sqrt(numpy.dot(v, v))
self.player_vel += (SimpleModel.MOVE_VEL * v)

And you can also simplify your code fragment above to:
model.player_pos += (1,0,0)

This fixes the problem. I'll have a look at what is going on with the
target tracking code tomorrow.

The target is also a 3D coord which would explain why it doesn't work
in the zaxis either.

jon.

Jonathan Hollocombe

unread,
Apr 17, 2010, 4:28:38 PM4/17/10
to gumms-r...@googlegroups.com
OK, so:

if numpy.any(numpy.abs(self.target[:2]) >
numpy.abs(self.player_pos[:2])):
v = self.target[:2] - self.player_pos[:2]
v *= 1/sqrt(numpy.dot(v, v))
self.player_vel[:2] += (SimpleModel.MOVE_VEL * v)

seems to have fixed it. I've stopped it from tracking on the zaxis as
this was messing up the jumping (and the idea is that the target would
be set by the mouse which can only click on the xy plane anyway) and
i've fixed it so it doesn't oscillate around the target point anymore.

If you change the code: model.player_pos += (1,0,0) to model.target +=
(1,0,0) it should now work.

Jonathan Hollocombe

unread,
Apr 18, 2010, 4:37:34 AM4/18/10
to gumms-r...@googlegroups.com
My last fix was just wrong. I'm still not entirely happy with it but
i've modified the model again so it actually works.

I've add a pointer image to show the player target and also added panning.

The controls are now:
> Move mouse to edge of screen to pan.
> Space to jump.
> Click to move.

Let me know if you have any questions/bugs to report.
models.py
iso_demo3.py

B W

unread,
Apr 18, 2010, 11:54:49 AM4/18/10
to gumms-r...@googlegroups.com
That's mo better. Demo 2 was driving me nuts, Couldn't figure it out and had to put it down.

With just the one sprite I'm getting between 833 and 1000 FPS. I'm very curious to see the tiles integrated. When will you try that?

A few observations, of which you may already be aware:

  1. This may already be your next step, but I want to vote. =) Add a field of tiles. I'm dying to see what kind of performance this construct will demonstrate.
  2. I'm a fan of having the avatar centered on the screen. Panning is a good option to support, but it can become cumbersome so one should have the option to easily switch the behavior. For example, if I move the mouse out of the window to multitask the view pans until I manage to change window focus. Could also be detrimental if I twitch to select a suddenly appearing baddy and the view moves, taking him away from the spot to which I'm wrenching the mouse pointer.
  3. While panning is enabled, you maybe shouldn't allow panning the avatar off the screen. Or at least provide an option for the game maker. That way if you write a RTS you can pan to your heart's content, for example. For a hero-centric action RPG we will potentially have to worry about "is it visible from here" and "have I mapped this yet, so show or mask the area?" problems. These could greatly impact performance when dealing with large maps.
  4. When I jump while moving, the arrow hits an invisible wall and slides down it. Obviously because it moves too fast to complete an entire arc. I know you probably will tune the jump physics. A couple options occur to me:
    1. Slow the sprite speed to allow the arc to complete naturally.
    2. Allow the avatar to jump and land past the target point.
  5. There will be other problems we have to solve, like depth-of-field.The order in which images are rendered can obscure other images. In some cases this is what you want, in others it is not. In the construct I cooked up this is still nagging me, and it will be a significant requirement. It's best to identify these early so we can maintain the awareness and not paint ourselves into a corner.
But foremost, great job. I don't think I could have come up with this. I eyeballed the matrix math with frustration. Wikipedia doesn't dumb it down enough for me.

You're working the numpy hard. How did you learn it and what are you using as a reference? The docs kind of suck, and yes that's an understatement. :) The lack of familiarity is greatly hampering my comprehension of your code.

Gumm

Jonathan Hollocombe

unread,
Apr 18, 2010, 12:28:10 PM4/18/10
to gumms-r...@googlegroups.com
On 18 April 2010 16:54, B W <stabbin...@gmail.com> wrote:
> That's mo better. Demo 2 was driving me nuts, Couldn't figure it out and had
> to put it down.
>
> With just the one sprite I'm getting between 833 and 1000 FPS. I'm very
> curious to see the tiles integrated. When will you try that?

Glad you like it. I'm going to be really busy until Wednesday so
really shouldn't do anything more on this until then (not that that
has stopped me for the last few days :-) )

My plan was to bring the floor tiles back into the demo for the next
stage. I agree that panning could be problematic for some games, I'll
set the default to no panning at the same time.

As for the jumping: do you think the character should return to the
target after over-jumping or that the target should be set to wherever
it lands? I can think of reasons for both tactics.

I also have plans for the physics (though these would take my
SimpleModel towards a much more complicated one):
> Acceleration + deceleration towards the target, with possible speed increase if holding a "run" button.
> Obstacles, 3D objects that can stop movement, possibly with some bounce off. Some will be short enough to jump over.

Anything else you can think of?

> But foremost, great job. I don't think I could have come up with this. I
> eyeballed the matrix math with frustration. Wikipedia doesn't dumb it down
> enough for me.
>
> You're working the numpy hard. How did you learn it and what are you using
> as a reference? The docs kind of suck, and yes that's an understatement. :)
> The lack of familiarity is greatly hampering my comprehension of your code.

Well doing Mathematics for a degree helped with the matrix stuff :-)
(though it's been a while and I'm pretty rusty). As for numpy, I've
done a reasonable amount of Matlab in my job so the way numpy works is
pretty familiar. The main thing to realise when reading the code is
that numpy.dot(A, B) is equivalent to A x B where 'x' is matrix
multiplication. I plan to write down the maths behind what I've done
fairly soon, partly for documentation and partly so i don't forget how
i did it.

cheers, jon.

B W

unread,
Apr 20, 2010, 12:43:44 AM4/20/10
to gumms-r...@googlegroups.com
Hi, Jon.

Yesterday I was working on map creation and loading. Performance of an experimental 100 x 43 tile map was so bad, it led me to look for optimizations. I put together a couple snippets (see attached) that I hope you'll find useful. They were pretty enlightening, and inspired me to make optimizations in the to/from-isometric methods for a 25% performance gain. They could certainly benefit from a lot more.

It was a busy day. I should have some time tomorrow to reply to your latest post.

Cheers.

Gumm
isometric_bench.py
fast_collide_circle.py

B W

unread,
Apr 21, 2010, 1:19:14 AM4/21/10
to gumms-r...@googlegroups.com
On Sun, Apr 18, 2010 at 9:28 AM, Jonathan Hollocombe <jholl...@googlemail.com> wrote:

My plan was to bring the floor tiles back into the demo for the next
stage. I agree that panning could be problematic for some games, I'll
set the default to no panning at the same time.

Looking forward to it. I posted the benchmark snippets primarily with this in mind. Like I mentioned, I found that my 2D to_isometric() and from_isometric() methods were very expensive. The loop that converts every 2D model tile to isometric space in order to test if the tile should be rendered began to melt down if many tiles were added. I also noticed your 3D version took about 10 times longer, which is counter to what I expected from numpy code, and I knew that if my computer was straining with the 2D version it would not be able to handle the 3D version.
 
As for the jumping: do you think the character should return to the
target after over-jumping or that the target should be set to wherever
it lands? I can think of reasons for both tactics.

I think that would depend on the play experience you wanted: arcade versus sim.
 
I also have plans for the physics (though these would take my
SimpleModel towards a much more complicated one):
> Acceleration + deceleration towards the target, with possible speed increase if holding a "run" button.
> Obstacles, 3D objects that can stop movement, possibly with some bounce off. Some will be short enough to jump over.

Anything else you can think of?

Yeah, what you mention are must haves.
  • Line of sight? So you can't shoot around corners.
  • Reflection? So you can bounce shots around corners or walls.
Not sure how hard these would be. Just trying to think of some bells-n-whistles that might be interesting in a game.
 
I've recently been fiddling with some new stuff:
  • Map files. These are currently ASCII maps, easy to code and read, though the aspect is off (stretched vertically due to font metrics). The game model can only handle one map at this time. Eventually it would handle lists of linked maps. I've made a few maps which you can activate by uncommenting in map0001.py. Including one huge map, which I can only run at about 30 FPS. :P Guess I need to devise a zoning algorithm.
  • Scaling. This is now inherent in the isometric view. The window calculates the scale according to a baseline height (600 pixels). All the Rabbyt sprites use this scale, and the isometric conversion uses it. That way the game view is equally proportionate to the window size. You can check this out by changing Window.size.
  • An optimization in the IsometricView class. I saw there was a significant amount of unnecessary recalculation every call. So I implemented properties for the largely static data, and a dirty attribute that is turned on the by setters. So these now get recalculated once per game tick instead of 1024 (which is a lot even on a smallish 32x32 map).
One thing appears to be a Rabbyt artifact. There is a moment while panning where partial tiles along the side edges aren't rendered. I've tried a couple tricks, but I cannot figure out how to work around this.

Gumm
iso_demo_b.zip

Jonathan Hollocombe

unread,
Apr 21, 2010, 2:06:43 PM4/21/10
to gumms-r...@googlegroups.com
I'm not surprised that the 3D conversion is slower than the 2D one as
the maths is more general and more complicated, though i am surprised
it's 10 times slower. I used numpy and the matrix style calculations
to keep the code similar to the maths i was working from; it should be
possible to simplify and possibly speed up the code quite a bit.

Saying that, i think the major problem is just the amount of 3D
objects that are off-screen that it's trying to convert to screen
coords (or at least it is in the code i am working on now). I am
attempting to create a mask in the 3D coords using the the screen size
so that only objects that are within this zone will be converted to
screen coords. This will reduce the number of to_isometric calls from
thousands to tens per loop (hopefully). It's not perfected yet but
it's getting there.

With 32x32 tiles i'm seeing 30 fps but with the mask it goes to 60.

I'm assuming 60 fps is the highest i'm going to see? It seems to be
capped at 60 on my machine which i assume is to do with my screen
refresh rate (60 Hz) and display.flip() pausing the loop until the
next refresh.

I've slowed down again with the code but hopefully i'll have something new soon.

B W

unread,
Apr 21, 2010, 10:51:57 PM4/21/10
to gumms-r...@googlegroups.com
With 32x32 tiles i'm seeing 30 fps but with the mask it goes to 60.

That's impressive. Can't wait to see how you do it.
 
I'm assuming 60 fps is the highest i'm going to see? It seems to be
capped at 60 on my machine which i assume is to do with my screen
refresh rate (60 Hz) and display.flip() pausing the loop until the
next refresh.

Sounds like the game loop is using clock.tick(60).

Gumm

B W

unread,
Apr 22, 2010, 9:26:26 PM4/22/10
to gumms-r...@googlegroups.com
Brief update on the problem with the tiles at the edges not rendering. It was my code, not Rabbyt.

I had not noticed that the artifact appeared when I added screen-proportionate scaling for sprites and to/from-isometric methods. The images and translations scaled accurately, but I forgot to also scale the pygame.Rect that I was using to test collision with the screen. So while the scaled image was on-screen, the rect was not and it left a hole in the scene.

In order to see it I had to borrow some OpenGL code from PyMike's library to get dots on the screen and study the rect in motion. It was driving me mad! :)

Gumm

B W

unread,
Apr 26, 2010, 1:34:13 AM4/26/10
to gumms-r...@googlegroups.com
Hi, Jon.

I made a few leaps and bounds. The attached ZIP contains all but the arrow images. Simply copy them to the directory /image/arrow/.

The old demo is still there, which switches between iso and top-down.

The new demo (iso_demo_g.py) opts for the simplicity of a single view. It adds:
  • Panning (independent avatar and camera) and snap-to-avatar. Use cursor and space keys.
  • Map loading. It is crude, but has full potential.
  • Zoning to limit the scope of updates and renders to the vicinity of the avatar. Good frame rates, even on the huge map.
Next I'll try to simplify usage with better abstraction in the game module.

I also need to figure out a way to link multiple maps (aka rooms or regions) and move between them in real-time. That will stretch my brain somewhat, I'm sure. But it will be necessary to keep map sizes manageable.

In the near future I will make an attempt at converting everything to MVC. Perhaps before I try multiple maps and the code grows more complex.

How's the 3D stuff coming along?

Cheers.

Gumm
isometric_demo_g.zip

B W

unread,
Apr 28, 2010, 2:11:15 AM4/28/10
to gumms-r...@googlegroups.com
Latest generation of 2D isometrics in MVC. I think this is a good baseline suitable for a SVN repo.

Same old fugly tiles, but hey. So far I've just been going for function.

Gumm
isometric_demo_h.zip

B W

unread,
Apr 29, 2010, 1:46:48 AM4/29/10
to gumms-r...@googlegroups.com
And here is a puzzle for you. At the end of event_manager.py I added the following:

if __name__ == '__main__':
    class Clock1(object):
        def __init__(self, em):
            self.em = em
            self.em.register(self)
        def receive(self, e):
            if e.type is TickEvent:
                print 'tick'
                pygame.time.wait(1000)
                self.em.post(TickEvent())
    class Clock2(object):
        def __init__(self, em):
            self.em = em
            self.em.register(self)
        def receive(self, e):
            if e.type is TickEvent:
                print 'tock'
    em = EventManager()
    clock1 = Clock1(em)
    clock2 = Clock2(em)
    em.post(TickEvent())
    quit()

It doesn't work right. It only prints 'tick' repeatedly. It should print two lines 'tick' and 'tock' repeatedly.  But if you switch the order:

    clock2 = Clock2(em)
    clock1 = Clock1(em)

it works. I can't figure it out and it's driving me nuts.

Gumm

Jonathan Hollocombe

unread,
Apr 29, 2010, 1:23:04 PM4/29/10
to gumms-r...@googlegroups.com
It looks like the Clock1 object is catching the Tick event, which
causes it to fire another Tick event which it then catches, and this
repeats ad nauseum. The order things are registered with the event
manager is important (with a single threaded program anyway, with a
multi-threaded one multiple events could get fired at the same time).

I'll try and catch up with your other emails tonight.

Cheers,
Jonathan.

B W

unread,
Apr 29, 2010, 2:35:51 PM4/29/10
to gumms-r...@googlegroups.com
Right. I see what you're saying. It's a recursion problem. The for loop in send() is never getting to the next iteration. Thanks for getting me past that mental block.

Gumm

B W

unread,
May 2, 2010, 1:56:58 AM5/2/10
to gumms-r...@googlegroups.com
Hi, Jon.

I'm occupying myself with investigating ways to make isometric tiles while waiting for your feedback. Finding it more of a challenge than I expected.

After a few hours of trying to paint diamonds and not doing a very good job, decided to try orthogonal paintings and see if I could apply transformations after loading the images. They are still a chore but much easier to draw, with their right angles. Attached is a couple small demos.

The Pygame one looks pretty cheap and trashy, and I couldn't close the gaps and get them all to line up right. I wrote it in hopes that I could load the orthogonal images, transform them, and then save them using Pygame--as a one-time manual conversion step when creating tile sets. It works, but the quality isn't as good as I'd hoped.

The Rabbyt one looks much nicer; but it too had its quirks. Saving the transformed images to file isn't an option either.

Let me know if this inspires any ideas.

Gumm
tile_transform.zip

B W

unread,
May 3, 2010, 11:53:53 PM5/3/10
to gumms-r...@googlegroups.com
Here's a demo with some tiles I made in GraphicsGale. They were a lot easier to make. I was hoping they would join better than the tiles made in GIMP, as GG's pointer snaps to grid and I could not figure out how to get GIMP to snap rather than use fractional end points when drawing lines. I made a base tile with dots for guides, then drew lines using the guides.

While this tile set does look noticeably nicer, the tiles do not match up perfectly in Pygame nor Rabbyt and you can still see a seam where Rabbyt anti-aliases the adjoining tiles.

I am quite ready to give up on 2D graphics for this. It is proving to be unwieldy, and the results lack the quality I crave.

OpenGL's ability to translate, transform, and apply textures in 3D is very enticing at this point. If we can handle the learning curve, it could open up so many possibilities.

I just saw that Rabbyt's web site seems to have dropped out of DNS. Best case, the owner was late renewing the subscription, and he will get it back. Worst case, Rabbyt go down da hole. Dunno what to expect.

Gumm
graphics_gale.zip

Jonathan Hollocombe

unread,
May 4, 2010, 4:27:01 PM5/4/10
to gumms-r...@googlegroups.com
Hi there,

Sorry I have been so poor at replying over the last few weeks, I've
recently started a new project at work which has drained all of my
mental energies so I've made next to no progress on any of the stuff I
was working on. I got to the point where i had mapped the screen into
my 3D model and could restrict the transforms to only those within
this area. Unfortunately the test for whether the object was inside
the area was as expensive as my 3D transform so it didn't help, even
though the maths was pretty simple. I think at some point we may need
to write some C extension modules to handle the most used functions in
order to get the kinds of speeds we'd like to see. I know how to do
this in Linux, but have no experience of it on Windows. In the
meantime I think I may just find the smallest rectangle that fits
around the mapped area so i can used the fast colliderect function.
Hopefully I can get back into it this weekend.

Can't say I can help much with the wall rendering problems, looking at
doing something similar was quite a long way down my list of things to
do so I don't have much experience to share. I have done some 3D stuff
a long time ago (Direct3D but should be similar) and would be
interested in exploring the possibility of using 3D rendering.

I think at some point it would be good to define some strict
boundaries between the sections (Model/View) and fix some APIs to make
it easier to swap around the models and views to find what works best.

Cheers.

B W

unread,
May 6, 2010, 9:12:43 PM5/6/10
to gumms-r...@googlegroups.com
On Tue, May 4, 2010 at 1:27 PM, Jonathan Hollocombe <jholl...@googlemail.com> wrote:
was working on. I got to the point where i had mapped the screen into
my 3D model and could restrict the transforms to only those within
this area. Unfortunately the test for whether the object was inside
the area was as expensive as my 3D transform so it didn't help, even
though the maths was pretty simple.

I ran into the same problem, the isometric translations were too expensive to use frivolously. I ended up adding "zones" to the 2D model. These are 4x4 groups of tiles which divide the map into larger rects so I can use Pygame to detect a collision with the screen. This rules out major portions of the map with just a few rect.colliderect tests. The zones that collide are the ones that I transform to the view coordinates for drawing. The other part of that trick, which you allude to later, was using a screen-rect that's a bit larger than the screen, since the isometric perspective is canted and shows more tiles than a top-down perspective.

In addition I saw a significant gain in testing for collisions a second time, which resulted in rendering fewer sprites with Rabbyt. For this I used Rabbyt's aabb_collide_single function, which is pretty efficient at returning a list of sprites that collide with a single object. Since the object only needs a top, bottom, left and right attribute it made sense to add these attributes to my Window class for this purpose.

These optimizations were sufficient to kick the FPS into high gear.
 
I think at some point we may need
to write some C extension modules to handle the most used functions in
order to get the kinds of speeds we'd like to see. I know how to do
this in Linux, but have no experience of it on Windows.

I was searching for some tutorials on this. It looks rather ugly, but hey, if it kicks butt then why not use it where it counts. I got as far is downloading the Open Watcom C compiler, then lost momentum.
 
Can't say I can help much with the wall rendering problems, looking at
doing something similar was quite a long way down my list of things to
do so I don't have much experience to share. I have done some 3D stuff
a long time ago (Direct3D but should be similar) and would be
interested in exploring the possibility of using 3D rendering.

I just wanted to show you what I'd done with my meager talents so you could say ew, this 2D tile stuff is probably not going to work out.
 
I think at some point it would be good to define some strict
boundaries between the sections (Model/View) and fix some APIs to make
it easier to swap around the models and views to find what works best.

A stable API sounds great in theory. :)

Gumm
Reply all
Reply to author
Forward
0 new messages