Welcome to TiddlyWiki created by Jeremy Ruston, Copyright © 2007 UnaMesa Association
Found this awesome reference to {{{itertools}}} via the comments in this blog post:
http://tartley.com/?p=1081
http://docs.python.org/library/itertools.html#itertools.product
{{{
import itertools
edge=1
verts=list(itertools.product(*([-edge/2,edge/2],)*3))
print verts
}}}
prints:
{{{
[(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (-1, 0, 0), (0, -1, -1), (0, -1, 0), (0, 0, -1), (0, 0, 0)]
}}}
Welcome to the PyGame Wiki! Nothing much interesting to report yet....
I started this as my online notes while I learn [[PyGame|http://www.pygame.org]], which is a set of [[Python|http://www.python.org/]] modules wrapping [[Simple DirectMedia Layer|http://www.libsdl.org/]] (SDL). As I write little games, I update it with tidbits of knowledge I figure out.
My background in scripting\programming is entirely self-taught: I first learned the scripting language MEL (Maya Embedded Language, where I have made a '[[Mel wiki|http://mayamel.tiddlyspot.com/]]', then picked up Python (see my [[Python Wiki|http://pythonwiki.tiddlyspot.com/]]) and have dabbled in [[Processing|http://www.processing.org]] (see my '[[Processing Wiki|http://processingwiki.tiddlyspot.com/]]'). Because of that, my approach to learning a language may be a bit different from the 'standard programmer'. I often find myself missing out on key pieces of programming lore, that take me longer than I presume normal to figure out.
''DISCLAIMER''
*I can't be blamed for anything misrepresented on this page! If it doesn't work, no finger-waving my direction.
*If you find something wrong, please tell me. I //am// trying to correctly learn this stuff.
*Since I work on a Windows system, the majority of the command line stuff I call out to is Win-centric. If you're using Lunix\Unix\OSX etc, I can't comment on how well things will work.
Below are a list of //all// the subjects (tiddlers) in the wiki. Note that there will be ones for site maintenance etc, that should be disregarded.
----
<<list all>>
*http://www.blackpawn.com/texts/pointinpoly/default.html (nice code example at bottom)
*http://en.wikipedia.org/wiki/Barycentric_coordinates_%28mathematics%29
*http://mathworld.wolfram.com/BarycentricCoordinates.html
<<gradient horiz #ffffff #ddddff #8888ff >>
The PyGame tiddlywiki blog
*[[2009 08 27]] - Welcome
These are sample methods to be used in a Sprite class (called to inside it's {{{Update()}}} method probably), for moving a sprite based on a direction and speed:
{{{
import math
class MySprite(pygame.sprite.Sprite):
# __init__(), update(), etc...
def rotate(self):
# save our "old" rect center
oldCenter = self.rect.center
# rotate our saved image by degrees, store as new image:
self.image = pygame.transform.rotate(self._image, self.degrees)
# Reset our rect based on the newly rotated image.
# It's size could have changed based on the rotation:
self.rect = self.image.get_rect()
# Reset the rects center, which is important if it was resized:
self.rect.center = oldCenter
def calcVector(self):
# turn degrees into radians
radians = self.degrees * math.pi / 180
# Calculate the delta in x and y position
self.dx = math.cos(radians)
self.dy = math.sin(radians)
# change the delta's based on current speed
self.dx *= self.speed
self.dy *= self.speed
# Flip the y, since it is opposite what PyGame expects:
self.dy *= -1
# finally, add the delta values to our positional vals:
self.x += self.dx
self.y += self.dy
}}}
http://wiki.yoyogames.com/index.php/Ari_Feldman%27s_Book_on_Game_Graphics
*"In 2000, Ari Feldman wrote a book called Designing Arcade Computer Game Graphics. Unfortunately, it was taken out of print (although copies still might exist in the retail channel). Ari Feldman has been so kind to give us the right to distribute the book electronically. In this way all of you can learn about how to create graphics for your games."
*http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/
*http://www.pygame.org/project/241/
*http://www.blackpawn.com/texts/pointinpoly/default.html
*http://realtimecollisiondetection.net/
*In PyGame, the top-left corner of the screen is {{{(x=0, y=0)}}}, and the bottom-right corner is {{{(x=1, y=1)}}}.
*But in PyMunk, the //bottom-left// corner is {{{(x=0, y=0)}}}, and the //top-right// corner is {{{(x=1, y=1)}}}.
To convert between the two, you can write a simple function:
{{{
def convertToPygame(pos, height):
# pos : tuple : (x,y)
# height : float : height of PyGame window
return (pos[0], height-pos[1])
}}}
http://directpython.sourceforge.net/
*"DirectPython is an open source C++ extension to the Python programming language which provides basic access to ~DirectX (9.0c) API, including ~Direct3D, ~DirectSound, ~DirectShow and ~DirectInput."
{{{
def distanceBetween(a, b):
# return the distance between the (x,y) positions of two sprites
return math.sqrt(abs(pow(b.x-a.x, 2) + pow(b.y-a.y, 2)))
}}}
Often times I'll want to draw a line based on some info: A point in space, a rotation value, and a length of line. Here's a code snippet:
{{{
import math
def degreesToRadians(deg):
return (deg * math.pi) / 180.0
# surf = some PyGame surface
# color = some Pygame color
length = 10
rotate = degreesToRadians(45)
p1 = (25,25)
p2 = (math.cos(rotate)*length+p1[0], -math.sin(rotate)*length+p2[1])
pygame.draw.line(surf, color, p1, p2)
}}}
Note how we need to flip the {{{math.sin}}} value: This is a difference in the coordinate spaces of the {{{math}}} module, and {{{pygame}}}.
I have a blog post with code examples showcasing this [[here|http://www.akeric.com/blog/?p=563]].
When dealing with the event queue, you're dealing with {{{Event}}} objects. These are objects made via {{{pygame.event.Event}}}. Docs:
http://www.pygame.org/docs/ref/event.html#pygame.event.Event
For a given {{{Event}}} object, you can query attributes for:
*{{{event.type}}} : returns back and event type constant. See [[Event Types]].
!Some Examples:
Presume that this code has been executed first:
{{{
import pygame
# This puts all constants into the current scope, so you
# don't need to qualify them with (for example) 'pygame.QUIT',
# you can just use 'QUIT'.
from pygame.locals import *
}}}
----
You can use:
{{{
pygame.event.wait()
}}}
Wait will sit and block further game execution until an event comes along. This is not generally very useful for games, as you want to be animating things at the same time.
http://www.pygame.org/docs/ref/event.html#pygame.event.wait
----
Using {{{pygame.event.poll()}}}:
It should be noted that this can be quite wasteful, since {{{event.poll}}} returns {{{NOEVENT}}} by default, and can be a resource hog.
"get a single event from the queue"
Returns a single {{{Event}}} object.
http://www.pygame.org/docs/ref/event.html#pygame.event.poll
{{{
while True:
event = pygame.event.poll()
if event.type == QUIT:
running = 0
elif event.type == MOUSEMOTION:
print "mouse at (%d, %d)" % event.pos
}}}
----
Using {{{pygame.event.get()}}}:
"get events from the queue"
Returns a list of {{{Event}}} objects.
http://www.pygame.org/docs/ref/event.html#pygame.event.get
{{{
while True:
for event in pygame.event.get():
if event.type == QUIT:
return
elif event.type == KEYDOWN:
if event.key == K_a:
# etc...
}}}
----
Another {{{pygame.event.get()}}} example to print events.
It should be noted the {{{def}}} needs to occur before the {{{while}}}:
{{{
def printEvent(events):
# Expecting a list of events
for event in events:
if event.type == QUIT:
sys.exit(0)
else:
print event
while True:
printEvent(pygame.event.get())
}}}
!Key Events:
A similar way to query events is querying which keys were pressed. Using the event querying methods above, if you hold a key down, it will only register a single "keypress" until you release the key. If you want something to repetedly happen while a key is held down, then you can use the below system:
{{{
while True:
# control tank motion:
keys = pygame.key.get_pressed()
if keys[K_UP]:
tank.updateVelocity(.1)
if keys[K_DOWN]:
tank.updateVelocity(-.1)
if keys[K_LEFT]:
tank.updateAngle(1.0)
if keys[K_RIGHT]:
tank.updateAngle(-1.0)
}}}
http://www.pygame.org/docs/ref/event.html
Event ''types'', and their queryable ''attributes''.
{{{
QUIT none
ACTIVEEVENT gain, state
KEYDOWN unicode, key, mod
KEYUP key, mod
MOUSEMOTION pos, rel, buttons
MOUSEBUTTONUP pos, button
MOUSEBUTTONDOWN pos, button
JOYAXISMOTION joy, axis, value
JOYBALLMOTION joy, ball, rel
JOYHATMOTION joy, hat, value
JOYBUTTONUP joy, button
JOYBUTTONDOWN joy, button
VIDEORESIZE size, w, h
VIDEOEXPOSE none
USEREVENT code
}}}
These are all attributes of the {{{pygame}}} module itself, so they can be accessed via:
{{{
import pygame
print pygame.ACTIVEEVENT
# 1
}}}
Or, if you import {{{pygame.locals}}} into the current namespace, you don't have to qualify the constants with the {{{pygame}}} namespace:
{{{
import pygame
from pygame.locals import *
print ACTIVEEVENT
# 1
}}}
{{{
"""
default01.py
Eric Pavey - 2009-08-08
Default, base, initial setup for a pygame program
"""
#--------------
# Imports & Initis
import sys
import pygame
from pygame.locals import *
pygame.init()
#--------------
# Constants
WIDTH = 512
HEIGHT = 512
FRAMERATE = 60
#-------------
# Screen Setup
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Default")
clock = pygame.time.Clock()
#-------------
# Define helper functions, classes
def foo():
pass
#------------
# Main Program
def main():
print "Running Python version:", sys.version
print "Running PyGame version:", pygame.ver
looping = True
# main loop
while looping:
# maintain our framerate:
clock.tick(FRAMERATE)
# detect for events
for event in pygame.event.get():
if event.type == pygame.QUIT:
looping = False
# Do stuff!
foo()
# update our display:
pygame.display.update()
#------------
# Execution from shell\icon:
if __name__ == "__main__":
main()
}}}
>Import Pygame, Pygame Locals, external Modules
>
>Define Constants
>
>Define Helper Functions
>
>Define Class Objects
>
>Create Screen, Clock
>
>Main Loop
>>Do Actions
>>Draw to Screen
>>Query Events
----
~IDEA-ALTER from 'Game Programming' (The L Line, The Express Line to Learning) by Andy Harris
*I - Import and Initialize
*D - Display configuration
*E - Entities (background, etc)
*A - Action (broken into ALTER):
**A - Assign values to key variables
**L - Setup main Loop
**T - Time - update clock
**E - Event handling
**R - Refresh display
This is still a tricky subject for me: I have yet to see any speed improvements using it. It's quite possible I'm simply doing it wrong. But, some notes in the meantime!
----
*You can query to see if your machine is hardware accelerated:
**http://www.pygame.org/docs/ref/display.html#pygame.display.Info
{{{
info = pygame.display.Info()
print info.hw
# 1
}}}
*The {{{Info}}} object has other hardware related values as well.
----
*The main screen can be made into a hardware [[Surface]]:
**It should be noted that you can only use a 'hardware surface' if you are in fullscreen (so say the docs). And you might as well 'double-buffer' it at that point:
**http://www.pygame.org/docs/ref/display.html#pygame.display.set_mode
{{{
screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.FULLSCREEN|pygame.DOUBLEBUF|pygame.HWSURFACE)
}}}
It should be noted that you should use:
http://www.pygame.org/docs/ref/display.html#pygame.display.flip
{{{
pygame.display.flip()
}}}
When using double-buffered, hardware surfaces, since it will 'flip' the two buffers using this call.
If using software surfaces, you can use:
http://www.pygame.org/docs/ref/display.html#pygame.display.update
{{{
pygame.display.update()
}}}
and its permutations to get better performance in software.
----
*Surfaces can also be made into hardware surfaces:
*http://www.pygame.org/docs/ref/surface.html
{{{
someSurf= pygame.Surface((w,h), flags=pygame.HWSURFACE)
}}}
**This will, quote "...creates the image in video memory".
----
*It seems though, if you want to monkey with the pixels on your surface, ''hardware surfaces are a bad idea'' (from the 'surface' docs):
>There is support for pixel access for the Surfaces. '''Pixel access on hardware surfaces is slow and not recommended''. Pixels can be accessed using the get_at() and set_at() functions. These methods are fine for simple access, but will be considerably slow when doing of pixel work with them. If you plan on doing a lot of pixel level work, it is recommended to use the pygame.surfarray module, which can treat the surfaces like large multidimensional arrays (and it's quite quick).
*Furthermore, if you are using //''{{{pygame.draw}}}''//, you may also want to steer away from hardware surfaces:
**http://www.pygame.org/docs/ref/draw.html
>Rendering to hardware Surfaces will be slower than regular software Surfaces.
There doesn't seem to be a //built in// way, but you can fake it bay scaling the surface twice: Scale it small once, then scale it back to normal size.
{{{
def blurSurf(surface, amt):
"""
Blur the given surface by the given 'ammount'. Only values 1 and greater
are valid. Value 1 = no blur.
"""
if amt < 1.0:
raise ValueError("Arg 'amt' must be greater than 1.0, passed in value is %s"%amt)
scale = 1.0/float(amt)
surf_size = surface.get_size()
scale_size = (int(surf_size[0]*scale), int(surf_size[1]*scale))
surf = pygame.transform.smoothscale(surface, scale_size)
surf = pygame.transform.smoothscale(surf, surf_size)
return surf
}}}
You can also use {{{pygame.transform.rotozoom()}}} in place of {{{smoothscale}}}. However, they have different effects: {{{smoothscale}}} appears to blur (and push) the pixels 'outward' based on the center of the surface, while {{{rotozoom}}} blurs (and pushes) them outward based on the top left corner of the image {{{(0,0)}}}, effectively making them translate down and to the right in the process.
http://www.pygame.org/docs/ref/transform.html#pygame.transform.smoothscale
http://www.pygame.org/docs/ref/transform.html#pygame.transform.rotozoom
Other methods I haven't investigated would involve running a convolve routine on the surface pixels. I have a feeling this would work around the issue of the "shifting" pixels the above systems have issues with.
Check out my blog page here for my {{{Tablet}}} class, and {{{pressureTest.py}}} example program:
http://www.akeric.com/blog/?page_id=798
Source code examples online here:
http://www.akeric.com/python/tablet/
http://www.pygame.org/docs/ref/time.html
Make a {{{Clock}}} object:
{{{
# usual setup pygame code omitted...
clock = pygame.time.Clock()
# in main loop:
while True:
# run at 30 fps:
clock.tick(30)
}}}
{{{clock.get_fps()}}} will return what you're after.
{{{
pygame.display.set_caption("fps: %.2f" % clock.get_fps() )
}}}
This will add it to the window title, using string formatting to only display the last two decimal places of the framerate.
Make a {{{pygame.display.Info()}}} object.
http://www.pygame.org/docs/ref/display.html#pygame.display.Info
Available attrs:
*{{{hw}}}: True if the display is hardware accelerated
*{{{wm}}}: True if windowed display modes can be used
*{{{video_mem}}}: The megabytes of video memory on the display. This is 0 if unknown
*{{{bitsize}}}: Number of bits used to store each pixel
*{{{bytesize}}}: Number of bytes used to store each pixel
*{{{masks}}}: Four values used to pack RGBA values into pixels
*{{{shifts}}}: Four values used to pack RGBA values into pixels
*{{{losses}}}: Four values used to pack RGBA values into pixels
*{{{blit_hw}}}: True if hardware Surface blitting is accelerated
*{{{blit_hw_CC}}}: True if hardware Surface colorkey blitting is accelerated
*{{{blit_hw_A}}}: True if hardware Surface pixel alpha blitting is accelerated
*{{{blit_sw}}}: True if software Surface blitting is accelerated
*{{{blit_sw_CC}}}: True if software Surface colorkey blitting is accelerated
*{{{blit_sw_A}}}: True if software Surface pixel alpha blitting is acclerated
*{{{current_w}}}, {{{current_h}}}: Width and height of the current video mode, or of the desktop mode if called before the display.set_mode is called. (current_h, current_w are available since SDL 1.2.10, and pygame 1.8.0) They are -1 on error, or if an old SDL is being used.
Implemented via {{{pygame.display.set_mode}}}
http://www.pygame.org/docs/ref/display.html#pygame.display.set_mode
{{{
# setup some constants:
WIDTH = 640
HEIGHT = 480
}}}
The easiest way is to just call to the {{{pygame.FULLSCREEN}}} constant:
{{{
screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.FULLSCREEN)
# then later in the main loop:
pygame.display.update()
}}}
But if you are running fullscreen, you also have the option of making the display 'double-buffered' for better performance, and you can take advantage of video-card hardware acceleration (presuming the video card is hardware accelerated). This is implemented via the additional {{{pygame.DOUBLEBUF}}} and {{{pygame.HWSURFACE}}} constants.
<<<
It should be noted that when 'double-buffering' the display, you need to call to {{{pygame.display.flip()}}} rather than {{{pygame.display.update()}}}, otherwise it just won't seem to work ;) Furthermore with my tests, if you make your display a hardware surface and //don't// double-buffer, I get a lot of screen flickering, so I have a feeling they should be used in conjunction.
<<<
{{{
screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.FULLSCREEN|pygame.DOUBLEBUF|pygame.HWSURFACE)
# then later inside the main loop:
pygame.display.flip()
}}}
Here's a handy chart:
| FULLSCREEN | | |display.update() | = | ''success'' |
| FULLSCREEN | | |display.flip() | = | ''success'' |
| FULLSCREEN | DOUBLEBUF | HWSURFACE |display.flip() | = | ''success'' |
| FULLSCREEN | DOUBLEBUF | |display.flip() | = | ''success'' |
| FULLSCREEN | | HWSURFACE |display.flip() | = | flickering |
| FULLSCREEN | | HWSURFACE |display.update() | = | flickering |
| FULLSCREEN | DOUBLEBUF | HWSURFACE |display.update() | = | fail |
| FULLSCREEN | DOUBLEBUF | |display.update() | = | fail |
http://www.pygame.org/docs/ref/display.html#pygame.display.flip
http://www.pygame.org/docs/ref/display.html#pygame.display.update
{{{
screen = pygame.display.set_mode((640, 480))
# later in your code...
surf = pygame.Surface(screen.get_size())
surf = surf.convert()
}}}
http://www.pygame.org/docs/ref/surface.html
Make one the same size as the screen. Let's call it 'drawlayer':
This will make the //whole surface transparent//, setting the ''surface alpha''. This is different than per-pixel alpha.
{{{
drawlayer = pygame.Surface(screen.get_size())
drawlayer.set_alpha(0)
drawlayer = drawlayer.convert_alpha()
}}}
http://www.pygame.org/docs/ref/surface.html#Surface.set_alpha
----
Here we set it up with ''per-pixel alpha'', and make the whole thing transparent:
{{{
drawLayer = pygame.surface.Surface(screen.get_size(), flags=pygame.SRCALPHA, depth=32)
drawLayer .convert_alpha()
drawLayer.fill((0,0,0,0))
}}}
<<<
The pixel format can be controlled by passing the bit depth or an existing Surface. The flags argument is a bitmask of additional features for the surface. You can pass any combination of these flags:
HWSURFACE, creates the image in video memory
SRCALPHA, the pixel format will include a per-pixel alpha
<<<
----
Filling a surface with a color, that could be transparent:
{{{
drawLayer.fill(color)
}}}
http://www.pygame.org/docs/ref/surface.html#Surface.fill
>The color argument can be either a RGB sequence, a RGBA sequence or a mapped color index. If using RGBA, the Alpha (A part of RGBA) is ignored unless the surface uses per pixel alpha (Surface has the SRCALPHA flag).
----
From the docs:
<<<
There are three types of transparency supported in Pygame: colorkeys, surface alphas, and pixel alphas. Surface alphas can be mixed with colorkeys, but an image with per pixel alphas cannot use the other modes. Colorkey transparency makes a single color value transparent. Any pixels matching the colorkey will not be drawn. The surface alpha value is a single value that changes the transparency for the entire image. A surface alpha of 255 is opaque, and a value of 0 is completely transparent.
Per pixel alphas are different because they store a transparency value for every pixel. This allows for the most precise transparency effects, but it also the slowest. Per pixel alphas cannot be mixed with surface alpha and colorkeys.
<<<
Languages like Processing have [[functions|http://www.processing.org/reference/map_.html]] that will take a given value from one range and map it into another. I can't seem to find this in Python, but here's the code:
{{{
def valueRangeMap(val, origMin, origMax, newMin, newMax):
"""
Will remap val from the original range into the new range.
val : The value you are mapping.
origMin : The original minimum value that val should be greater than.
origMax : The original maximum value that val should be less than.
newMin : The new minimum value that val will be mapped into.
newMax : the new maximum value that val will be mapped into.
return : float : The newly mapped value.
"""
# Find the percentage val is between origMin and origMax:
percetVal = float(val - origMin) / float(origMax - origMin)
# Map into the new range:
mappedVal = (newMin+newMax)*percetVal
return mappedVal
print valueRangeMap(5, 0, 10, 10, 20)
# 15.0
}}}
From the [[PyGame FAQ|http://www.pygame.org/wiki/FrequentlyAskedQuestions]]
To define where the PyGame window starts:
{{{
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "10,10"
}}}
The var is a string comprised of two ints separated by a comma, relative to the top left corner of the Windows display. Realize this is placing the top left corner of the PyGame display screen, not the frame around the window. If you passed in {{{"0,0"}}}, the frame would be off the screen, making it hard to move, close, etc.
----
Another method, from [[this post|http://www.mail-archive.com/pygame-users@seul.org/msg03464.html]], which requires the use of the [[pywin32 package|http://sourceforge.net/projects/pywin32/]]:
{{{
hwnd = pygame.display.get_wm_info()["window"]
import win32gui
import win32con
x, y = (200, 200)
win32gui.SetWindowPos(hwnd, 0, x, y, 0, 0, win32con.SWP_NOSIZE | win32con.SWP_NOZORDER)
}}}
----
To center the screen, set the env var before you ini Pygame:
{{{
import os
import pygame
os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.init()
}}}
----
Also see:
*[[Query screen resolution]]
Two ways so far....
Method #1: Check for each "press occurrence":
Check if a key is //pressed//. This will only turn {{{keyPressed}}} 'True' during the physical act of //pressing the key//. It does not track if you keep holding it down. Picture if you will a paint-program: Using this method to query if the 'c' key has been pressed, and if so, draw a circle, it will only draw //one circle per press//. It won't draw if the key is //held down continuously// (since this only tracks the act of pressing the key).
{{{
# inside main loop:
keyPressed = False
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_c:
keyPressed= True
}}}
Method #2: Check if a key is //held down//. From our paint-program example from above, this method //does// track if the 'c' key is physically //held down//, and will continue to draw our circles as long as you hold the key down.
{{{
# inside main loop:
keyHeld = False
keys = pygame.key.get_pressed()
if keys[K_c]:
keyHeld = True
}}}
----
''Querying for modifier keys:''
See the full list here: [[Key constants]].
If you want to see if someone has pressed a key like {{{ctrl}}}, {{{alt}}}, (etc), along with some other key, you need to check for which //modifiers// have been pressed:
For example, let's see if someone has pressed {{{ctrl+z}}} (using either left or right {{{ctrl}}} key):
{{{
# inside the main loop:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_z:
mods = pygame.key.get_mods()
if mods == KMOD_LCTRL or mods == KMOD_RCTRL:
# do stuff
}}}
If multiple mods are pressed at the same time, their 'kmod' values are simple added as well. If you wanted someone to press left {{{ctrl}}} //and// left {{{shift}}} with the {{{z}}} key:
{{{
if pygame.key.get_mods() == KMOD_LCTRL + KMOD_LSHIFT:
# do stuff
}}}
Two ways so far....
Method #1: Check for each "press occurrence":
Check if a mouse button has been //pressed//: This will only turn {{{mousePressed}}} 'True' during the physical act of pressing the button. It does not track if you keep holding it down. Picture if you will a paint-program: using this method to query if the mouse has been pressed, and if so, draw a circle, it will only draw one circle per click. It won't draw if the button is held down (since this only tracks the act of pressing the button).
{{{
# inside main loop:
mousePressed = False
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
mousePressed = True
}}}
Method #2: Check if the button is //held down//. From our paint-program example from above, this method //does// track if the button is physically //held down//, and will continue to draw our circles as long as you hold the button down.
{{{
# inside main loop:
mouseHeld = False
mouseButtons = pygame.mouse.get_pressed()
if mouseButtons[0] or mouseButtons[1] or mouseButtons[2]:
mouseHeld = True
}}}
----
Also see:
*[[Mouse buttons]]
Presuming you're have [[cgkit|http://cgkit.sourceforge.net/]] installed (Windows only):
{{{
import cgkit
tabletConnected = cgkit.wintab.info(WTI_INTERFACE)['NDEVICES']
# 1 if tablet is detected, 0 otherwise
}}}
{{{
import os
pygamedir = os.path.split(pygame.base.__file__)[0]
print pygamedir
# c:\Python26\lib\site-packages\pygame
}}}
!!!Querying
You can //query// this value on some systems via this method:
http://www.pygame.org/docs/ref/display.html#pygame.display.get_wm_info
{{{
windowData = pygame.display.get_wm_info()
hwnd = windowData.get("window")
# 1377614
}}}
Since it's possible that the dictionary may not have the {{{"window"}}} key, we use the dictionary {{{get()}}} method, since it will return {{{None}}} rather than raising a {{{KeyError}}}
!!!Setting
If you don't know the ID, you can make up your own. //Then// you need to update the SDL env var:
{{{
hwnd = 1234
os.environ["SDL_WINDOWID"] = str(hwnd)
# later you can pass hwnd into the code that requires it...
}}}
I know very little about this ;) Some apps need to know the 'window ID' of the PyGame window, also known as the 'window handle' / 'hwnd'.
[[Surfaces|Surface]] have no built in method to 'tint' them. But below are two different ways to do it.
Presuming the image we are loading has alpha in it
{{{
surface = pygame.image.load(path).convert_alpha()
color = pygame.color.Color("orange")
}}}
Long method:
{{{
tintSurf = pygame.Surface(surface.get_size()).convert_alpha()
tintSurf.fill(color)
surface.blit(tintSurf, (0,0), special_flags=BLEND_RGBA_MULT)
}}}
Short method:
{{{
surface.fill(color, special_flags=BLEND_RGBA_MULT)
}}}
In this example, we'll make the default screen half the resolution of the monitor:
To start, setup our screen, and some background surface:
{{{
info = pygame.display.Info()
SCREEN_SIZE = (info.current_w/2, info.current_h/2)
screen = pygame.display.set_mode(SCREEN_SIZE, pygame.RESIZABLE)
background = pygame.Surface(SCREEN_SIZE)
}}}
The key is the {{{pygame.RESIZABLE}}} arg.
Then later in the main loop:
{{{
for event in pygame.event.get():
if event.type == pygame.VIDEORESIZE:
SCREEN_SIZE = event.size
screen = pygame.display.set_mode(SCREEN_SIZE, pygame.RESIZABLE)
background = pygame.Surface(SCREEN_SIZE)
}}}
When the {{{VIDEORESIZE}}} event is detected, we can query the new size, then remake our screen surface, and background surface, with the new size.
<<gradient horiz #ddddff #ffffff >>This is a [[TiddlyWiki|http://tiddlywiki.com/]]. To really get a grasp on how to navigate it, check out their [[homepage|http://tiddlywiki.com/]]. Quick directions below:
----
''SEARCHING FOR DATA:''
#You can do key-word searches in the search-field in the top of the //right column//.
#Browse the "Tags" tab in the //right column// for Python-ish key-words.
**Inside the Tags tab, major ''Categories'' are all in caps, like "[[EVENTS]]".
**When picking a 'Tag' with more than one link, you can either:
###'{{{Open all}}}' the topics in that Tag (meaning, fully expand all the topics listed in the middle of that pop-up menu).
###Open a single topic by picking its heading.
###Show all the headings in the Tag by picking: {{{Open tag 'tagname}}}'.
#Use your web browsers screen-search ability ('Ctrl+F' in both Firefox & Internet Explorer) to find key-words you're after (good if 'Tags' tab is open).
#Or start browsing from the ''Categories'' section in the //left column//. This will open each of their major headings in a new tiddler.
If things get too crowded, use the "close all" from the //right column// to clean up the page. Or, you can use "close others" from an individual tiddler (This block called "[[Instructions For Use]]" is a tiddler, for example).
----
''COPYING DATA FROM WIKI, TO PYTHON:''
*The way the text has been entered into this wiki, copying code from the source-boxes should work:
{{{
source-code in a box;
}}}
*Other times it's not in a box, but is still safe for copy:
{{{source-code not in a box, but still safe to copy;}}}
*If you copy any code outside of a box that's 'not safe', Python //may// have a hard time with it's formatting and be angered. Weird
----
FYI: for subject 'tagging' purposes, I specifically leave off the {{{pygame}}} namespace from all objects, functions, and constants, since it is presumed all code in here is based around {{{pygame}}} (presuming it's a {{{pygame}}} module. Others, like {{{pymunk}}}, have their module namespaces added). Plus it will save a lot of typing.
So, for example, rather than make a tag called '{{{pygame.time}}}', it would just be '{{{time}}}'.>>
All the keyboard key constants (can be used in [[event querying|Event Querying Methods]]):
http://www.pygame.org/docs/ref/key.html
These are all attributes of the {{{pygame}}} module itself, so they can be accessed via:
{{{
import pygame
print pygame.K_a
# 97
}}}
Or, if you import {{{pygame.locals}}} into the current namespace, you don't have to qualify the constants with the {{{pygame}}} namespace:
{{{
import pygame
from pygame.locals import *
print K_a
# 97
}}}
----
{{{
Letters:
K_a ... K_z
Numbers:
K_0 ... K_9
Control:
K_TAB
K_RETURN
K_ESCAPE
K_SCROLLOCK
K_SYSREQ
K_BREAK
K_DELETE
K_BACKSPACE
K_CAPSLOCK
K_CLEAR
K_NUMLOCK
Punctuation:
K_SPACE
K_PERIOD
K_COMMA
K_QUESTION
K_AMPERSAND
K_ASTERISK
K_AT
K_CARET
K_BACKQUOTE
K_DOLLAR
K_EQUALS
K_EURO
K_EXCLAIM
K_SLASH, K_BACKSLASH
K_COLON, K_SEMICOLON
K_QUOTE, K_QUOTEDBL
K_MINUS, K_PLUS
K_GREATER, K_LESS
Brackets:
K_RIGHTBRACKET, K_LEFTBRACKET
K_RIGHTPAREN, K_LEFTPAREN
F-Keys:
K_F1 ... K_F15
Edit keys:
K_HELP
K_HOME
K_END
K_INSERT
K_PRINT
K_PAGEUP, K_PAGEDOWN
K_FIRST, K_LAST
Keypad:
K_KP0 ... K_KP9
K_KP_DIVIDE
K_KP_ENTER
K_KP_EQUALS
K_KP_MINUS
K_KP_MULTIPLY
K_KP_PERIOD
K_KP_PLUS
SHF,CTL,ALT etc:
K_LALT, K_RALT
K_LCTRL, K_RCTRL
K_LSUPER, K_RSUPER
K_LSHIFT, K_RSHIFT
K_RMETA, K_LMETA
Arrows:
K_LEFT
K_UP
K_RIGHT
K_DOWN
Other:
K_MENU
K_MODE
K_PAUSE
K_POWER
K_UNDERSCORE
K_HASH
K_UNKNOWN
Modifiers:
KMOD_NONE
KMOD_LSHIFT
KMOD_RSHIFT
KMOD_SHIFT
KMOD_CAPS
KMOD_LCTRL
KMOD_RCTRL
KMOD_CTRL
KMOD_LALT
KMOD_RALT
KMOD_ALT
KMOD_LMETA
KMOD_RMETA
KMOD_META
KMOD_NUM
KMOD_MODE
}}}
/***
|''Name:''|LoadRemoteFileThroughProxy (previous LoadRemoteFileHijack)|
|''Description:''|When the TiddlyWiki file is located on the web (view over http) the content of [[SiteProxy]] tiddler is added in front of the file url. If [[SiteProxy]] does not exist "/proxy/" is added. |
|''Version:''|1.1.0|
|''Date:''|mar 17, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#LoadRemoteFileHijack|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
***/
//{{{
version.extensions.LoadRemoteFileThroughProxy = {
major: 1, minor: 1, revision: 0,
date: new Date("mar 17, 2007"),
source: "http://tiddlywiki.bidix.info/#LoadRemoteFileThroughProxy"};
if (!window.bidix) window.bidix = {}; // bidix namespace
if (!bidix.core) bidix.core = {};
bidix.core.loadRemoteFile = loadRemoteFile;
loadRemoteFile = function(url,callback,params)
{
if ((document.location.toString().substr(0,4) == "http") && (url.substr(0,4) == "http")){
url = store.getTiddlerText("SiteProxy", "/proxy/") + url;
}
return bidix.core.loadRemoteFile(url,callback,params);
}
//}}}
<<gradient horiz #ddddff #ffffff >>
*[[Welcome]]
*[[About PyGame Wiki]]
*[[Instructions For Use]]
*[[Latest Updates|History]]
*[[PyGame Links]]
*[[Blog]]
*[[About Author|WarpCat]]
----
[[All Subjects]]
----
''Categories:''
[[3D]]
[[AI]]
[[EVENTS]]
[[EXT RESOURCE]]
[[FUNDAMENTALS]]
[[INFO]]
[[INTERACTION]]
[[MATH]]
[[NAVIGATION]]
[[PERFORMANCE]]
[[PIXELS]]
[[PYMUNK]]
[[SCREEN DISPLAY]]
[[TRANSFORMATION]]
What I coin a collision object in PyMunk is a combination of a rigid body, and a collision shape. It should be noted that (I have read) a rigid body can have one or more collision shapes added to it, but I don't do that in this example.
Presume the user has already got PyMunk imported, and setup:
{{{
import pymunk
pymunk.init_pymunk()
space = pymunk.Space()
}}}
Let's do something simple, like a circle.
!Make Rigid Body:
http://pymunk.googlecode.com/svn/trunk/docs/api/pymunk.Body-class.html
First, we need to make it's ''inertia'', which PyMunk comes pre-loaded with:
{{{
inertia = pymunk.moment_for_circle(mass, 0, radius, (0,0))
}}}
Next, we create our ''rigid body'', including the mass (a float)
{{{
rigidBody = pymunk.Body(mass, inertia)
}}}
We can set its default position if we'd like. {{{pos}}} is a tuple: {{{(x,y)}}}. It should be noted that PyMunk's (0,0) point is in the lower left corner, while PyGame's is in the top left. To do your conversion between coordinates, you need to do some hoopjumping like this (presume {{{HEIGHT}}} is a constant defining the display's height):
{{{
pos = pygame.mouse.get_pos()
rigidBody.position = (pos[0], HEIGHT-pos[1])
}}}
!Make Collision Shape:
http://pymunk.googlecode.com/svn/trunk/docs/api/pymunk.Circle-class.html
Now that the rigid body has been setup, we need to make a ''collision shape'' for it. We'll use PyMunk's default {{{Circle}}} shape, passing in the {{{rigidBody}}} we just made, a float for the radius of the circle, and the 'center of mass' offset value (which we'll set to 0,0, meaning, the middle of the circle):
{{{
colShape = pymunk.Circle(rigidBody, radius, (0,0))
# The rigidBody is now accessed via the colShape.body attr
}}}
Other things can now be set on the collision shape, like frictional (float, 0.0->1.0) values:
{{{
colShape.friction = friction
}}}
!Add to Space:
http://pymunk.googlecode.com/svn/trunk/docs/api/pymunk.Space-class.html
Finally, we need to add both our {{{rigidBody}}} and {{{colShape}}} to the active {{{pymunk.Space()}}} object that should have been previously created:
{{{
space.add(rigidBody, colShape)
}}}
!Accessing Data:
The above code could be wrappered into a simple function, that returns the {{{colShape}}}. A list could then be used to collect all the shapes for processing.
If you wanted to draw a PyGame circle at the location of a given shape, you could use this code. Presume that {{{color}}} is some PyGame Color object, {{{screen}}} is the display or background Surface, and {{{HEIGHT}}} is a constant defining the height of the display.
{{{
# remember to flip the Y axis when going back to PyGame:
pos = (int(colShape.body.position.x), HEIGHT-int(colShape.body.position.y))
pygame.draw.circle(screen, color, pos, int(colShape.radius), 0)
}}}
Tutorialw:
*http://www.cs.iupui.edu/~aharris/pygame/Appendix_C.pdf
*http://www.blog.pythonlibrary.org/2010/07/31/a-py2exe-tutorial-build-a-binary-series/
Troubleshooting:
*http://blog.thadeusb.com/weblog/2009/4/15/pygame_font_and_py2exe
*http://hg.thadeusb.com/public/.r/Games/MyRTS/file/4dfc6b0d398b/src/setup.py (old, broken link)
Python Docs:
*http://docs.python.org/distutils/apiref.html#distutils.core.setup
Need to download\install:
*http://www.py2exe.org/
*http://docs.python.org/using/windows.html#py2exe
Notes:
*Put the program in its own directory
*Be sure to use external fonts.
From commandline:
{{{
python setup.py py2exe
}}}
----
----
Fonts seem to be a pain when making executables. Presuming you're using the default pygame font, you'll need to make sure your {{{setup.py}}} file points to these additional items:
*{{{SDL_ttf.dll}}}
*{{{zlib1.dll}}}
*{{{libfreetype-6.dll}}}
*{{{pygame.font.get_default_font()}}} (only if you're using the default font)
If you're using a custom {{{ttf}}} font, you'll need to add it to the dir you're making the executable from, and be sure to add its name to the file list.
!!!Resource:
*As a point of reference, [[chapter 9|http://books.google.com/books?id=_ztrTBEDK28C&lpg=PA191&ots=r56b7bLPzY&dq=python%20transformation%20matrix&pg=PA181#v=onepage&q=python%20transformation%20matrix&f=false]] from the book [[Beginning Game Development with Python and PyGame: From Novice to Professional|http://www.apress.com/book/view/9781590598726]] has a really well described overview.
**It has a {{{matrix44}}} class in its [[gameobjects|http://code.google.com/p/gameobjects/]] module. [[gameobjects documentation|http://www.willmcgugan.com/gameobjects/docs/index.html]]. [[matrix44 documentation|http://www.willmcgugan.com/gameobjects/docs/gameobjects.matrix44.Matrix44-class.html]]. It stores its translation values along the bottom row.
*{{{PyEuclid}}} that has a matrix class, but I have yet to do any research into it. But it does appear to store its translation values along the right column.
**Main Page: http://partiallydisassembled.net/euclid.html
**Documentation: http://partiallydisassembled.net/euclid/
**Source: http://code.google.com/p/pyeuclid/
!!!Math:
Psudo-code: Given two matrices, mA and mB, that are a 'grid' of 4x4 items, indexed from 0-3, here is the math when multiplying them together.
The values are:
matrix _ column (x) _ row (y)
{{{
mmult = [ mB_0_0 * mA_0_0 + mB_0_1 * mA_1_0 + mB_0_2 * mA_2_0 + mB_0_3 * mA_3_0,
mB_0_0 * mA_0_1 + mB_0_1 * mA_1_1 + mB_0_2 * mA_2_1 + mB_0_3 * mA_3_1,
mB_0_0 * mA_0_2 + mB_0_1 * mA_1_2 + mB_0_2 * mA_2_2 + mB_0_3 * mA_3_2,
mB_0_0 * mA_0_3 + mB_0_1 * mA_1_3 + mB_0_2 * mA_2_3 + mB_0_3 * mA_3_3,
mB_1_0 * mA_0_0 + mB_1_1 * mA_1_0 + mB_1_2 * mA_2_0 + mB_1_3 * mA_3_0,
mB_1_0 * mA_0_1 + mB_1_1 * mA_1_1 + mB_1_2 * mA_2_1 + mB_1_3 * mA_3_1,
mB_1_0 * mA_0_2 + mB_1_1 * mA_1_2 + mB_1_2 * mA_2_2 + mB_1_3 * mA_3_2,
mB_1_0 * mA_0_3 + mB_1_1 * mA_1_3 + mB_1_2 * mA_2_3 + mB_1_3 * mA_3_3,
mB_2_0 * mA_0_0 + mB_2_1 * mA_1_0 + mB_2_2 * mA_2_0 + mB_2_3 * mA_3_0,
mB_2_0 * mA_0_1 + mB_2_1 * mA_1_1 + mB_2_2 * mA_2_1 + mB_2_3 * mA_3_1,
mB_2_0 * mA_0_2 + mB_2_1 * mA_1_2 + mB_2_2 * mA_2_2 + mB_2_3 * mA_3_2,
mB_2_0 * mA_0_3 + mB_2_1 * mA_1_3 + mB_2_2 * mA_2_3 + mB_2_3 * mA_3_3,
mB_3_0 * mA_0_0 + mB_3_1 * mA_1_0 + mB_3_2 * mA_2_0 + mB_3_3 * mA_3_0,
mB_3_0 * mA_0_1 + mB_3_1 * mA_1_1 + mB_3_2 * mA_2_1 + mB_3_3 * mA_3_1,
mB_3_0 * mA_0_2 + mB_3_1 * mA_1_2 + mB_3_2 * mA_2_2 + mB_3_3 * mA_3_2,
mB_3_0 * mA_0_3 + mB_3_1 * mA_1_3 + mB_3_2 * mA_2_3 + mB_3_3 * mA_3_3 ]
}}}
I pulled this, and reformatted it, from the {{{gameobjects matrix44}}} class referenced above.
!!!Imagery:
*I made an image describing the above pseudo-code. You can find it on my [[blog here|http://www.akeric.com/blog/?p=693]], or on [[flickr here|http://www.flickr.com/photos/warpcat/3956108191/]]
Docs on the [[Surface]] object:
http://www.pygame.org/docs/ref/surface.html
Notes from doc:
<<<
A pygame Surface is used to represent any image. The Surface has a fixed resolution and pixel format. Surfaces with 8bit pixels use a color palette to map to 24bit color.
Call {{{pygame.Surface}}} for representing images to create a new image object. The Surface will be cleared to all black. The only required arguments are the sizes. With no additional arguments, the Surface will be created in a format that best matches the display Surface.
<<<
For use below, we make a 'screen' to modify pixels on.
{{{
# pygame.display.set_mode returns a 'Surface' object
screen = pygame.display.set_mode((width, height))
}}}
!Set the color of a single pixel:
http://www.pygame.org/docs/ref/surface.html#Surface.set_at
{{{
screen.set_at((x, y), (red, green, blue))
}}}
If you are doing many sets, it may be advantageous to '{{{Surface.lock()}}}' before the loop, and '{{{Surface.unlock()}}}' afterwords. This is only if you're using a hardward surface (which the screen often is).
!Query the color of a single pixel:
http://www.pygame.org/docs/ref/surface.html#Surface.get_at
http://www.pygame.org/docs/ref/color.html
{{{
# returns a Color object:
col = screen.get_at((x,y))
}}}
!Edge detection:
{{{
pygame.transform.laplacian
}}}
http://www.pygame.org/docs/ref/transform.html#pygame.transform.laplacian
!Average colors of multiple Surfaces:
{{{
pygame.transform.average_surfaces
}}}
http://www.pygame.org/docs/ref/transform.html#pygame.transform.average_surfaces
!Get threshold color:
{{{
pygame.transform.threshold
}}}
http://www.pygame.org/docs/ref/transform.html#pygame.transform.threshold
!Surface.blit options:
All part of the {{{pygame}}} namespace
>~BLEND_ADD, ~BLEND_SUB, ~BLEND_MULT, ~BLEND_MIN, ~BLEND_MAX ~BLEND_RGBA_ADD, ~BLEND_RGBA_SUB, ~BLEND_RGBA_MULT, ~BLEND_RGBA_MIN, ~BLEND_RGBA_MAX ~BLEND_RGB_ADD, ~BLEND_RGB_SUB, ~BLEND_RGB_MULT, ~BLEND_RGB_MIN, ~BLEND_RGB_MAX
http://www.pygame.org/docs/ref/surface.html#Surface.blit
Example:
{{{
screen.blit(surf, (x, y), None, pygame.BLEND_ADD)
}}}
----
| Left Mouse Button | {{{'button': 1}}} |
| Middle Mouse Button | {{{'button': 2}}} |
| Right Mouse Button | {{{'button': 3}}} |
| Scroll Wheel 'up\forward' | {{{'button': 4}}} |
| Scroll Wheel 'down\backward' | {{{'button': 5}}} |
| Side Button 'A' | {{{'button': 6}}} |
| Side Button 'A' | {{{'button': 7}}} |
You can use use this code to see if mouse buttons 1-3 are being held down:
{{{
# inside main loop:
mouseButtons = pygame.mouse.get_pressed()
# returns tuple (but1, but2, but3)
}}}
But for other mouse buttons, you have to query the event list:
{{{
# inside main loop:
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
if event.button == 4:
pass
}}}
----
Also see:
*[[How can I query if a mouse button has been pressed or held?]]
Nice overview on blog:
*Part 1: http://tartley.com/?p=1081
*Part 2: http://tartley.com/?p=1207
----
*http://pyopengl.sourceforge.net/
----
From [[this blog post|http://tartley.com/?p=1312]]
*[[Learning Modern 3D Graphics Programming Through OpenGL|http://www.arcsynthesis.org/gltut/index.html]] (C)
*[[An intro to modern OpenGL|http://duriansoftware.com/joe/An-intro-to-modern-OpenGL.-Table-of-Contents.html]] (C)
*[[Learning WebGL|http://learningwebgl.com/blog/?category_name=lessons]] (Javascript)
And, there is the first link slowly being ported to Python:
*[[https://bitbucket.org/tartley/gltutpy]]
!Don't redraw the whole screen every frame:
If you can avoid it, try not to do a {{{Surface.fill()}}} at every frame (for say a background): If you can instead only update the modified pixels, with say, {{{Surface.set_at()}}}, it should be much more efficient.
Phil's ~PyGame Utilities
http://code.google.com/p/pgu/
*Tools for editing tile-based levels
*A module for creating GUIs
*A set of general-purpose game libraries
/***
|''Name:''|PasswordOptionPlugin|
|''Description:''|Extends TiddlyWiki options with non encrypted password option.|
|''Version:''|1.0.2|
|''Date:''|Apr 19, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#PasswordOptionPlugin|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
***/
//{{{
version.extensions.PasswordOptionPlugin = {
major: 1, minor: 0, revision: 2,
date: new Date("Apr 19, 2007"),
source: 'http://tiddlywiki.bidix.info/#PasswordOptionPlugin',
author: 'BidiX (BidiX (at) bidix (dot) info',
license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
coreVersion: '2.2.0 (Beta 5)'
};
config.macros.option.passwordCheckboxLabel = "Save this password on this computer";
config.macros.option.passwordInputType = "password"; // password | text
setStylesheet(".pasOptionInput {width: 11em;}\n","passwordInputTypeStyle");
merge(config.macros.option.types, {
'pas': {
elementType: "input",
valueField: "value",
eventName: "onkeyup",
className: "pasOptionInput",
typeValue: config.macros.option.passwordInputType,
create: function(place,type,opt,className,desc) {
// password field
config.macros.option.genericCreate(place,'pas',opt,className,desc);
// checkbox linked with this password "save this password on this computer"
config.macros.option.genericCreate(place,'chk','chk'+opt,className,desc);
// text savePasswordCheckboxLabel
place.appendChild(document.createTextNode(config.macros.option.passwordCheckboxLabel));
},
onChange: config.macros.option.genericOnChange
}
});
merge(config.optionHandlers['chk'], {
get: function(name) {
// is there an option linked with this chk ?
var opt = name.substr(3);
if (config.options[opt])
saveOptionCookie(opt);
return config.options[name] ? "true" : "false";
}
});
merge(config.optionHandlers, {
'pas': {
get: function(name) {
if (config.options["chk"+name]) {
return encodeCookie(config.options[name].toString());
} else {
return "";
}
},
set: function(name,value) {config.options[name] = decodeCookie(value);}
}
});
// need to reload options to load passwordOptions
loadOptionsCookie();
/*
if (!config.options['pasPassword'])
config.options['pasPassword'] = '';
merge(config.optionsDesc,{
pasPassword: "Test password"
});
*/
//}}}
See notes on my Python Wiki:
{{{http://pythonwiki.tiddlyspot.com/#[[Rigid%20Body%20Physics%20in%20Python]]}}}
(copy-paste the link...)
@@''This page is a copy of one I had started over on my [[Python Wiki|http://pythonwiki.tiddlyspot.com/]] when I first started this one. It is a collection of my original notes, before this wiki expanded. Should probably just be deleted :)''@@
http://www.pygame.org/docs/
''Old Notes I need to organize in this wiki'':
----
Save a PyGame program as an executable:
http://www.pygame.org/wiki/Pygame2exe
----
Whole bunch of the below info was pulled from: http://rene.f0o.com/mywiki/LectureThree
----
Why do this? I've noticed PyGame modules with this at the header:
{{{
from pygame.locals import *
}}}
{{{/pygame/locals.py}}} contains a "Set of functions from PyGame that are handy to have in the local namespace for your module". Specifically:
{{{
from pygame.constants import *
from pygame.rect import Rect
import pygame.color as color
Color = color.Color
}}}
----
''Initialize all imported Pygame modules''
http://pygame.org/docs/ref/pygame.html#pygame.init
{{{pygame.init()}}} -> {{{\pygame\base.pyd}}}
{{{pygame.init(): return (numpass, numfail)}}}
*You may want to initialize the different modules seperately to speed up your program or to not use things your game does not.
*It is safe to call this init() more than once: repeated calls will have no effect.
----
''Set the resolution of the window:''
http://pygame.org/docs/ref/display.html#pygame.display.set_mode
{{{pygame.display.set_mode(resolution=(0,0), flags=0, depth=0): return Surface}}}
{{{
window = pygame.display.set_mode((468, 60))
}}}
----
''Set Window title''
pygame.display.set_caption('PyGame Window')
{{{pygame.display.set_caption(title, icontitle=None): return None}}}
{{{
pygame.display.set_caption('PyGame Window')
}}}
----
''Get the display window''
http://pygame.org/docs/ref/display.html#pygame.display.get_surface
{{{pygame.display.get_surface(): return Surface}}}
{{{
screen = pygame.display.get_surface()
}}}
----
''Load an image'':
http://pygame.org/docs/ref/image.html#pygame.image.load
{{{pygame.image.load(filename): return Surface}}}
{{{{pygame.image.load(fileobj, namehint=""): return Surface}}}
{{{
img = pygame.image.load(imgPath)
}}}
Uses relative pathing based on the location of the source module.
----
''Draw image to screen:'':
http://pygame.org/docs/ref/surface.html#Surface.blit
{{{Surface.blit(source, dest, area=None, special_flags = 0): return Rect}}}
{{{
screen.blit(img, (0,0))
}}}
----
''Update the surface to the screen'':
http://pygame.org/docs/ref/display.html#pygame.display.flip
{{{pygame.display.flip(): return None}}}
{{{
pygame.display.flip()
}}}
http://www.pygame.org/ctypes/pygame-api/pygame.constants-module.html
''Official Web'':
*http://www.pygame.org : main
**http://www.pygame.org/docs/ : documentation front page
**http://www.pygame.org/docs/ref/ : full language reference
**http://www.pygame.org/wiki/tutorials : tutorials
**http://www.pygame.org/download.shtml : download
**http://www.pygame.org/ctypes/pygame-api/pygame.constants-module.html : PyGame constants
''Forums'':
*http://groups.google.com/group/pygame-mirror-on-google-groups : Google Groups mirror of the ~PyGame mailing list.
''External tutorials I've pulled from: ''
*http://rene.f0o.com/mywiki/PythonGameProgramming
*http://en.wordpress.com/tag/pygame-tutorial/ (specifically the ones by Lorenzo E. Danielsson)
*Many more....
''Forums:''
*http://z7.invisionfree.com/pygame/
''PyGame books I've read:'':
*[[Beginning Game Development with Python and PyGame: From Novice to Professional|http://www.apress.com/book/view/9781590598726]]
*Link to the [[gameobjects|http://code.google.com/p/gameobjects/]] module used in that book, and its [[documentation|http://www.willmcgugan.com/gameobjects/docs/index.html]]
*[[Game Programming: The L Line, The Express Line to Learning|http://www.wiley.com/WileyCDA/WileyTitle/productCd-0470068221.html]]
''Online Books'':
*[[Making Games with Python|http://inventwithpython.com/pygame/]] : "... covers the Pygame library with the source code for 11 games. 'Making Games' was written as a sequel for the same age range as 'Invent with Python'".
*[[Invent Your Own Computer Games With Python|http://inventwithpython.com/]] : "...is a free e-Book that teaches you how to program in the Python programming language. Each chapter gives you the complete source code for a new game, and then teaches the programming concepts from the example."
*[[The "Invent With Python Blog"|http://inventwithpython.com/blog/]] is the blog for the above book. It has a great series of [[Code Comments|http://inventwithpython.com/blog/?s=Code+Comments]] tutorials showing how to recreate classic games, with the code fully commented.
''Similar to PyGame''
*[[pyglet|http://www.pyglet.org/]] : Pure Python ~OpenGL goodness.
*[[Processing|http://processing.org/]] : Extension of [[Java|http://java.sun.com/]]
*[[OpenFrameworks|http://www.openframeworks.cc/]] : [[c++|http://en.wikipedia.org/wiki/C%2B%2B]]
*[[Cinder|http://libcinder.org/]] : c++
*[[Field|http://openendedgroup.com/field/]] : [[Processing|http://processing.org/]] + [[Jython|http://www.jython.org/]] (Mac only)
*[[NodeBox|http://nodebox.net]] : Python based, Mac only. Similar API to Processing.
**[[NodeBox2|http://beta.nodebox.net/]] Python based, Windows or Python.
**[[Nodebox for OpenGL|http://www.cityinabottle.org/nodebox/]] Python \ pyglet based, API similar to Processing.
http://pymedia.org/
*"~PyMedia is a Python module for wav, mp3, ogg, avi, divx, dvd, cdda etc files manipulations. It allows you to parse, demutiplex, multiplex, decode and encode all supported formats. It can be compiled for Windows, Linux and cygwin."
*PyMunk Source:
**http://code.google.com/p/pymunk/
*PyMunk API Docs:
**http://pymunk.googlecode.com/svn/trunk/docs/api/index.html
*PyMunk Example:
**http://code.google.com/p/pymunk/wiki/SlideAndPinJointsExample
*Seems to also require http://www.pyglet.org/ in these demo files, PyGame for everything else:
**{{{box2d_vertical_stack.py}}}
**{{{demo_moon_main.py}}}
**As of //this// authoring pyglet only runs on Python versions 2.4 and 2.5
----
*Chipmunk Source:
**http://code.google.com/p/chipmunk-physics/
*Chipmunk Docs:
**http://code.google.com/p/chipmunk-physics/wiki/Documentation
*Chipmunk Forums:
**http://www.slembcke.net/forums/viewforum.php?f=1
*Chipmunk Tutorials:
**http://www.alexandre-gomes.com/articles/chipmunk/
----
Notes:
----
* ''[[spaces|http://pymunk.googlecode.com/svn/trunk/docs/api/pymunk.Space-class.html]]'': Spaces are the basic simulation unit in Chipmunk. You add bodies, shapes and joints to a space, and then update the space as a whole.
*''[[rigid bodies|http://pymunk.googlecode.com/svn/trunk/docs/api/pymunk.Body-class.html]]'': A rigid body holds the physical properties of an object. (mass, position, rotation, velocity, etc.) It does not have a shape by itself. If you’ve done physics with particles before, rigid bodies differ mostly in that they are able to rotate.
*''[[collision shapes|http://pymunk.googlecode.com/svn/trunk/docs/api/pymunk.Shape-class.html]]'': By attaching shapes to bodies, you can define the a body’s shape. You can attach many shapes to a single body to define a complex shape, or none if it doesn’t require a shape.
*''[[joints|http://pymunk.googlecode.com/svn/trunk/docs/api/pymunk.Joint-class.html]]'': You can attach joints between two bodies to constrain their behavior.
----
First, always init pymunk:
{{{
import pymunk as pm
pm.init_pymunk()
}}}
When you import pymunk, you're actually bringing this file into your current namespace:
{{{C:\Python26\Lib\site-packages\pymunk\__init__.py}}}
...since {{{pymunk}}} is a [[package|http://pythonwiki.tiddlyspot.com/#Packages]]. You can inspect that {{{__init__.py}}} module to see how {{{pymunk}}} authors all of its main classes and functions, good stuff.
----
!pymunk.Space
http://pymunk.googlecode.com/svn/trunk/docs/api/pymunk.Space-class.html
You need to __make a space__ for the sim to run in:
{{{
space = pm.Space()
# default args: iterations=10, elastic_iterations=10
}}}
__Set gravity__. Gravity expects a tuple, or a Vec2d
{{{
space.gravity((0.0, -900))
}}}
__Add bodies and shapes__ (and joints) to it:
{{{
body = pm.Body(mass, inertia)
shape = pm.Circle(body, radius, (0,0))
space.add(body, shape)
}}}
__Remove one or many shapes, bodies or joints__ from the space. Should remove both the shape and a body from the space
{{{
space.remove(shape, shape.body)
}}}
__Update the space__ for the given time step. Usually at the end of the pygame main loop:
{{{
space.step(1/60.0)
}}}
!pymunk.Body
http://pymunk.googlecode.com/svn/trunk/docs/api/pymunk.Body-class.html
''Methods:''
*{{{__init__(self, mass, inertia)}}}
*{{{apply_impulse(self, j, r=(0, 0))}}} : Apply the impulse j to body with offset r.
*{{{reset_forces(self)}}} : Reset the forces on the body
*{{{apply_force(self, f, r=(0, 0))}}} : Apply (accumulate) the force f on body with offset r.
*{{{update_velocity(self, gravity, damping, dt)}}} : Updates the velocity of the body using Euler integration.
*{{{update_position(self, dt)}}} : Updates the position of the body using Euler integration.
*{{{local_to_world(self, v)}}} : Convert body local to world coordinates
*{{{world_to_local(self, v)}}} : Convert world to body local coordinates
*{{{damped_spring(self, b, anchor1, anchor2, rlen, k, dmp, dt)}}} : Apply a spring force between this and body b at anchors anchr1 and anchr2 respectively.
''Properties:'' (attributes)
*{{{mass}}} : float
*{{{moment}}} : takes below:
**{{{pymunk.moment_for_circle(mass, inner_radius, outer_radius, offset=(0, 0))}}}
**{{{pymunk.moment_for_poly(mass, vertices, offset=(0, 0))}}}
*{{{angle}}} : in radians, the current rotational amount. Positive values are counter-clockwise motions.
*{{{rotation_vector}}}
*{{{torque}}}
*{{{position}}} : position.x, position.y or (x,y). Y axis has to be modified to work with PyGame: " {{{y = screen.get_size()[1] - body.position.y}}} "
*{{{velocity}}} : [[PyMunk Vec2d|http://pymunk.googlecode.com/svn/trunk/docs/api/pymunk.vec2d.Vec2d-class.html]] : Used to make body move around, impacted by the space's damping.
*{{{angular_velocity}}} : float : rotation velocity, impacted by space's damping. Positive values are counter-clockwise motions.
*{{{velocity_func}}}
*{{{position_func}}}
!pymunk.Shape
This is the base Shape class, inherited by Circle, Poly, and Segment
http://pymunk.googlecode.com/svn/trunk/docs/api/pymunk.Shape-class.html
''Methods:''
* none really (?)
''Properties'':
*{{{id}}} :
*{{{collision_type}}} :
*{{{group}}} :
*{{{layers}}} :
*{{{elasticity}}} :
*{{{friction}}} : float : friction of the surface. > 0, not sure what the high end is.
*{{{surface_velocity}}} :
*{{{body}}} : Reference to a rigid body.
!!!pymunk.Circle (inherits from Shape)
''Properties''
*{{{radius}}} :
*{{{center}}} :
!!!pymunk.Poly (inherits from Shape)
''Methods'':
*{{{get_points}}} :
''Properties'':
* none (?)
!!!pymunk.Segment (inherits from Shape)
''Properties'':
*{{{a}}}
*{{{b}}}
*{{{radius}}}
http://pyopengl.sourceforge.net/
"PyOpenGL is the cross platform Python binding to ~OpenGL and related ~APIs. The binding is created using the standard (in Python 2.5 and above) ctypes library, and is provided under an extremely liberal ~BSD-style ~Open-Source license."
http://inventwithpython.com/blog/2011/10/07/pygame-cheat-sheet/
http://inventwithpython.com/pygamecheatsheet.png
How to do? Based on [[this subject|How can I position the PyGame window on screen? Center it?]] you can control its starting position. But once its been moved after creation, how can you query that new position?
----
If you check out my blog here:
http://www.akeric.com/blog/?page_id=814
I have a class ({{{PygameWindowInfo}}}) you can download to do this. See the docs and info there.
----
Here is a way to do it on Windows. I got a bunch of this from the Python docs here:
http://docs.python.org/library/ctypes.html
Pseudo code, leaving out all the other Pygame bits
{{{
from ctypes import POINTER, WINFUNCTYPE, windll
from ctypes.wintypes import BOOL, HWND, RECT
# get our window ID:
hwnd = pygame.display.get_wm_info()["window"]
# Jump through all the ctypes hoops:
prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))
paramflags = (1, "hwnd"), (2, "lprect")
GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags)
# finally get our data!
rect = GetWindowRect(hwnd)
print "top, left, bottom, right: ", rect.top, rect.left, rect.bottom, rect.right
# bottom, top, left, right: 644 98 124 644
}}}
It should be pointed out that this is the outer extents of the //window//, not the internalized Pygame display screen (Surface). You'd need to add the width of the left border and the width of the title bar to accurately capture where the top-left corner of the Pygame screen is.
----
I found this post on how to do it with the underlying SDL on Windows:
http://www.gamedev.net/community/forums/topic.asp?topic_id=375551
{{{
// SDL code
static SDL_SysWMinfo pInfo;
SDL_VERSION(&pInfo.version);
SDL_GetWMInfo(&pInfo);
RECT r;
GetWindowRect(pInfo.window, &r); // now r contains the windows size and position
// uncommet this to set the position of the window
// SetWindowPos(pInfo.window, 0, r.left, r.top, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}}}
{{{
import pygame
xDir, yDir = pygame.mouse.get_rel()
xPos, yPos = pygame.mouse.get_pos()
}}}
{{{pygame.mouse.get_rel()}}} has to be treated carefully: It tracks the relative change of the mouse from the last time it was called. If you call it twice in a row, the 2nd call would return back zero values. Normally you'd put it inside of your main loop.
This will query the resolution of your display (monitor, flatscreen, etc), not the active PyGame screen (but to do so, it must be called before {{{pygame.set_mode()}}}).
{{{
import pygame
info = pygame.display.Info()
res = (info.current_w, info.current_h)
print res
# (1920, 1200)
# Now you can call to pygame.set_mode(x,y)
}}}
----
Also see:
*[[How can I position the PyGame window on screen? Center it?]]
http://www.pygame.org/docs/ref/rect.html
[[Rect]] and [[Surface]] objects are closely related: The [[Rect]] could be considered a 'picture frame' that can be moved around. The [[Surface]] is //the picture// that lives inside the frame. A picture can't be 'moved' unless it first lives in a frame.The frame stores position data, the picture stores the visual information.
>"Pygame uses Rect objects to store and manipulate rectangular areas. A Rect can be created from a combination of left, top, width, and height values. Rects can also be created from python objects that are already a Rect or have an attribute named "rect". "
{{{Rect}}} object ''attrs'' that can be queried //and// assigned to:
{{{
top, left, bottom, right
topleft, bottomleft, topright, bottomright
midtop, midleft, midbottom, midright
center, centerx, centery
size, width, height
w,h
}}}
They have these ''methods'':
*{{{colliderect(rect2)}}}
*{{{collidepoint(point)}}}
*{{{inflate(x,y)}}}
*{{{move(dx,dy)}}}
*many more, see doc link above...
How to get Rect objects:
----
{{{Surface.get_rect()}}}
http://www.pygame.org/docs/ref/surface.html#Surface.get_rect
----
Many things take rects as args. You can often pass in a rect object, or just pass in a series of values defining 'xcoord, ycoord, width, height'. these are acceptable ways of doing it:
Arg method:
{{{
rectA = (128, 128, 64, 64)
rectB = ((32, 32), (16, 16))
}}}
Rect method:
{{{
rectC = Rect(128, 128, 64, 64)
rectD = Rect((32, 32), (16, 16))
}}}
----
Also see:
*[[Rotational Basics]]
http://reinerstileset.4players.de/englisch.html
*"Thousands of game images for free use, by Reiner Prokein"
It's good to get rid of off-screen PyMunk items. Here's one way:
First, detect if the item has moved off-screen. The items in the {{{itemList}}} are presumed to be subclasses of {{{pymunk.Shape}}} {{{(pymunk.Circle}}}, {{{pymunk.Poly}}}, {{{pymunk.Segment}}}). The space is a pre-created {{{pygame.Space}}}. Their position (part of {{{pymunk.vec2d}}}) is compared against the screen size, factoring in their radius.
{{{
def removal_check(itemList, space):
for item in itemList:
if -item.radius > item.body.position.x or item.body.position.x > WIDTH+item.radius or -item.radius > item.body.position.y or item.body.position.y > HEIGHT+item.radius:
remove_item(item, space, itemList)
}}}
To do the actual removal, we create another function. Again, {{{item}}} is some subclass of {{{pygame.Shape}}}, {{{space}}} is same as above, and so is {{{itemList}}}. The item (collision shape) and its rigid body are first removed from the {{{space}}}. Then the item (collision shape) is removed from the current list of shapes:
{{{
def remove_item(item, space, itemList):
space.remove(item, item.body)
itemList.remove(item)
}}}
In PyGame:
*2d positive rotations happen in a counter-clockwise direction. Negative rotations are in a clockwise direction.
*"zero" degrees could be imagined to be aiming the center of a clock, pointing at "3-o'clock": It is an angle from the center point, directly right.
*"PyGame uses a 'mathematically grounded system' for working with angles, compared to a "navigational system". In the nav system, "zero-degrees" is "straight up" at 12-o'clock (the y-axis), and positive directions are //clockwise//. PyGame measures angles starting at the x-axis (3-o'clock), and positive falues move in a //counterclockwise// direction"
----
To rotate a [[Surface]] you can use {{{transform.rotate}}}
http://www.pygame.org/docs/ref/transform.html#pygame.transform.rotate
It should be noted that:
*This returns a new [[Surface]] object.
*There is a good chance the [[Surface]] object's sizes has been changed: If you rotate a square shaped image, in reality, its overall size increases to include the rotated size of the rectangular shape: In PyGame, [[Rect]] can never be "rotated" (they can be translated, and scaled though), they always stay rectangles aligned to the x and y axis.
Because of this, after a [[Surface]] has been rotated, you need to recalculate its corresponding [[Rect]]. Here's an example of this process from a [[sprite]] object method:
{{{
def rotate(self):
# Save the current center of the rect:
oldCenter = self.rect.center
# Update our image by rotating a temp _image by the given degrees.
self.image = pygame.transform.rotate(self._image, self.degrees)
# Get the *new sized* rect for our rotated Surface:
self.rect = self.image.get_rect()
# Update the new rect center with the old center, since there is a good
# chance it has changed due to the size change
self.rect.center = oldCenter
}}}
----
Also see:
*[[Calculating position based on angle and speed]]
http://www.pygame.org/docs/ref/image.html#pygame.image.save
Sample code. Save an image if the user presses the "s" key:
{{{
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_s:
pygame.image.save(screen, "test.png")
print "save test.png"
}}}
This is a function I had made for my [[BubblePaint|http://www.akeric.com/blog/?p=601]] program. In a nutshell, it will save the screen to a {{{png}}} image, and increment the filename with each image in the dir.
{{{
import glob, os, re
def saveImage():
"""
Save the current image to the working directory of the program.
"""
currentImages = glob.glob(".\\*.png")
numList = [0]
for img in currentImages:
i = os.path.splitext(img)[0]
try:
num = re.findall('[0-9]+$', i)[0]
numList.append(int(num))
except IndexError:
pass
numList = sorted(numList)
newNum = numList[-1]+1
saveName = 'bubblePaint.%04d.png' % newNum
print "Saving %s" % saveName
pygame.image.save(screen, saveName)
}}}
Will save out files like this:
{{{
bubblePaint.0001.png
bubblePaint.0002.png
etc...
}}}
Python seems to have no builtin 'serial' module\package. But searching the Python docs here:
http://docs.python.org/3.1/faq/library.html#how-do-i-access-the-serial-rs232-port
Leads one to ''pySerial'': http://pyserial.sourceforge.net/
pySerial Class docs: http://pyserial.sourceforge.net/pyserial_api.html#classes
Simple pseudo-code example, working with serial communication on ~COM5 (Which on my machine, is where my [[Arduino|http://www.arduino.cc/]] lives...)
{{{
import serial
ser = serial.Serial('COM5', timeout=1)
# Inside main loop:
while True:
clock.tick(30)
# Clean the buffer before we read it, so we only get the
# most recently received item:
ser.flushInput()
serialValue = ser.readline().strip()
print serialValue
}}}
I've found that putting this into a while loop //external// to Pygame to continuously print data just locks up the shell. However, this code //does// work in Pygame, which has better control over the framerate.
<<tabs txtMainTab Tags 'All tags' TabTags >>
<<tabs txtMainTab Dummy Dummy TabDummy Timeline Timeline TabTimeline All 'All tiddlers' TabAll More 'More lists' TabMore>>
notes on learning the language...
http://www.mechanicalcat.net/richard/log/Python/Solving_Game_Entity_Navigation_Using_Python
Just a link to this great blog post by Richard Jones. It uses the [[cocos2d|http://cocos2d.org/]] lib rather than Pygame, but I think the concepts are transferable.
http://home.gna.org/oomadness/en/soya3d/index.html
"Soya 3D is an object oriented "high level" 3D engine for Python. Somehow, Soya is to 3D what Python is to programming: an 'avant garde' 3D engine, a kind of 'UFO' in the 3D world :-). Soya allows to develop very rapidly games of other 3D apps, entirely in the Python language (contrary to most of the other engine, in which Python is limited to scripting tasks)."
*http://conkerjo.wordpress.com/2009/06/13/spatial-hashing-implementation-for-fast-2d-collisions/
!Directly compare collision between two sprites:
http://www.pygame.org/docs/ref/rect.html#Rect.colliderect
{{{
if spriteA.rect.colliderect(spriteB.rect):
print "Collide!"
}}}
!Compare collision between a sprite, and all sprites in a sprite group:
http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.spritecollide
{{{
if pygame.sprite.spritecollide(spriteA, spriteGroup, False):
print "Collide!"
}}}
{{{spritecollide}}} supports an optional fourth argument called "{{{collided}}}" that accepts these sprite functions as callbacks for determining more specific collision:
*{{{sprite.collide_rect}}}, {{{sprite.collide_rect_ratio}}}, {{{sprite.collide_circle}}}, {{{sprite.collide_circle_ratio}}}, {{{sprite.collide_mask}}}
*I should note that based on my tests, {{{sprite.collide_circle}}} seems to be //very// inaccurate. I've written my own (simply comparing the distance between two sprites to their combined "radius" attrs) which works flawlessly. Not sure why pygames doesn't: It seems to use collision circles 3/4 the size listed? Odd.
You can also use {{{pygame.sprite.spritecollideany}}}, which states that it 'can be faster, since it has less work to do'. I'm guessing this is because it has no additional arguments ({{{dokill}}}, {{{collide}}}) that have to be considered?
http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.spritecollideany
!Compare collision between two sprite groups:
http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.groupcollide
{{{
if pygame.sprite.groupcollide(group1, group2, dokill1, dokill2):
print "Collide!"
}}}
!Check for collision between a sprite and a sprite group, by comparing 'masks'.
This gives us //pixel perfect collision detection//. But its safe to say its slower than just comparing their rects... It appears (from looking at the docs) that {{{sprite.spritecollide}}} is the only sprite group that supports this kind of testing.
http://www.pygame.org/docs/ref/mask.html
http://www.pygame.org/docs/ref/surface.html#Surface.set_colorkey
http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.spritecollide
http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.collide_mask
The sprites in question need to have a mask generated. This is presuming they either already have alpha, or colorkey data to generate a mask from.
{{{
# this is usually added to the __init__() of the Sprite object
self.mask = pygame.mask.from_surface(self.image)
}}}
A function needs to be generated that can test whether or not two masks are overlapping. This can optionally be sidestepped, and replaced with a {{{lambda}}} (both shown below).
{{{
def maskCollide(left, right):
return pygame.sprite.collide_mask(left, right)
}}}
Finally, that test is passed into our group collision detection function (probably in the main loop):
{{{
# using our maskCollide() function:
col = pygame.sprite.spritecollide(spriteA,
spriteGroup,
False,
maskCollide)
if len(col):
# do work...
# using lambda instead:
col = pygame.sprite.spritecollide(spriteA,
spriteGroup,
False,
lambda x,y: pygame.sprite.collide_mask(x, y))
if len(col):
# do work...
}}}
It appears first it detects if the rects collide, and if so, it then tests to see if the masks collide.
@@''IMPORTANT''@@ : It appears that if you end up //rotating// your source sprite's {{{.image}}} attr's [[Surface]], you need to //re-compute// your mask from that [[Surface]]. In my tests using the below example (showing an example method for a Sprite object), this can //really slow down// the system, since every frame its regenerating the masks. This is a very lazy way to do the mask update, since it does it every frame no matter what. See the next section for a more efficient way...
{{{
def rotate(self):
oldCenter = self.rect.center
self.image = pygame.transform.rotate(self._image, self.degrees)
# Remake the mask, based on new image:
self.mask = pygame.mask.from_surface(self.image)
self.rect = self.image.get_rect()
self.rect.center = oldCenter
}}}
!Rotating Mask Collisions, Take 2:
Presuming you have //rotating// sprites you want to test mask collision on, here's another way that works much more efficiently compared to the above sections example.
#Make a Sprite object method that recreates its mask when called to.
#Make a function that tests for two mask collision and rebuilds the masks.
##Only call to that when the //rects// collide.
Sprite method that recreates the mask:
{{{
def updateMask(self):
self.mask = pygame.mask.from_surface(self.image)
}}}
Function that calls to two sprite methods to regenerate their masks, and then compares them:
{{{
def maskCollide(left, right):
# Will rebuild the masks of the given sprites, and then
# test for mask-based collision.
try:
left.updateMask()
except:
pass
try:
right.updateMask()
except:
pass
return pygame.sprite.collide_mask(left, right)
}}}
Code in the 'main loop' that does the actual collision detection. There are two sprite groups: The {{{mainSpriteGroup}}} that has the objects the user moves around (say, the vehicles) and the {{{blockGrp}}}, which has the things to collide with.
{{{
collisions = []
for srcSprite in mainSpriteGrp.sprites():
# RECT based collision first:
col = pygame.sprite.spritecollide(srcSprite, blockGrp, False)
if len(col):
for dest in col:
# Do MASK based filtering for only those collisions.
if pgSprites.VehicleBase.maskCollide(srcSprite, dest):
blockGrp.remove(dest)
}}}
This system is more efficient than than that last example in the previous section since it would be tested for at every frame for every sprite (slooooow)... This only runs our mask regeneration and test code if the rects first collide, speeding things up greatly.
----
http://www.flyingyogi.com/fun/spritelib.html
*"~SpriteLib GPL a collection of static and animated graphic objects (also commonly known as sprites). It was created to provide hobbyist game developers with an assortment of images to use in their creations. Because of ~SpriteLib GPL, developers don't have to waste precious time or money creating graphics from scratch."
http://www.pygame.org/docs/ref/surface.html
http://www.pygame.org/docs/ref/display.html
[[Rect]] and [[Surface]] objects are closely related: The [[Rect]] could be considered a 'picture frame' that can be moved around. The [[Surface]] is //the picture// that lives inside the frame. A picture can't be 'moved' unless it first lives in a frame. The frame stores position data, the picture stores the visual information.
From [[A Nuwbie Guide to Python|http://www.pygame.org/docs/tut/newbieguide.html]] (with some modifications by myself):
*The most important part of pygame is the ''surface''. Just think of a surface as a blank piece of paper. You can do a lot of things with a surface - you can draw lines on it, fill parts of it with color, copy images to and from it, and set or read individual pixel colors on it. A surface can be any size (within reason) and you can have as many of them as you like (again, within reason).
*''One surface is special'' - the one you create with {{{pygame.display.set_mode()}}}. This '[[display]] surface' represents //the screen//; whatever you do to it will appear on the user's screen. @@You can only have one of these@@ (display screen that is)- that's an SDL limitation, not a pygame one.
*So how do you create surfaces?
**You create the //special 'display surface'// with {{{pygame.display.set_mode()}}}.
**You can create a surface that contains an image by using {{{image.load()}}}.
**You can make a surface that contains text with {{{font.render()}}}.
**You can even create a surface that contains nothing at all with {{{Surface()}}}.
*Most of the surface functions are not critical. Just learn {{{blit()}}}, {{{fill()}}}, {{{set_at()}}} and {{{get_at()}}}, and you'll be fine.
*A pygame Surface is used to represent any image. The Surface has a fixed resolution and pixel format. Surfaces with 8bit pixels use a color palette to map to 24bit color.
Upon creation, Surfaces have two flags available (part of the {{{pygame}}} namespace):
*{{{HWSURFACE}}} - creates the image in video memory. Creates a 'hardware' surface, which //may// be faster than a nonhardware surface. (better not to set this flag and leave it up to pygame?)
*{{{SRCALPHA}}} - the pixel format will include a per-pixel alpha. Must set depth arg to 32.
Surface from scratch, just transparent alpha:
{{{
alphaSurface = pygame.Surface((512, 512), flags=pygame.SRCALPHA, depth=32)
}}}
----
Also see:
*[[Rotational Basics]]
http://www.spriters-resource.com/
http://www.clarku.edu/~djoyce/trig/
http://www.euclideanspace.com/maths/geometry/trig/index.htm
http://en.wikipedia.org/wiki/Trigonometric_functions
Given a [[right-triangle|http://en.wikipedia.org/wiki/Right_triangle]]:
*''theta'': The angle. Rotation amount in PyGame.
*''[[hypotenuse|http://en.wikipedia.org/wiki/Hypotenuse]]'': The longest side, opposite the "right angle". It "touches" the adjacent at the 'angle'. Can also be considered the "radius" of the circle being considered in PyGame, or the 'length of the vector'.
*''adjacent'': It "touches" hypotenuse at the angle. Can be considered a line along the x-axis in PyGame.
*''opposite'': Connects the hypotenuse and adjacent. It is "opposite" the angle. Can be considered a line along the y-axis in PyGame.
{{{
Pygame coordinates:
-- +X
|
|
+Y
}}}
Right Triangle: ("{{{0}}}" in the below graphic is theta)
{{{
/|
/ |
hyp / |
/ | opp
/ |
0 --------
adj
}}}
''SOHCAHTOA''!!!!
The ratios of these values have meaning:
*''SOH'' : ''s''in(theta) = ''o''pp / ''h''yp - "The //sine// of theta is the opposite divided by the hypotenuse".
*''CAH'' : ''c''os(theta) = ''a''dj / ''h''yp - "The //cosine// of theta is the adjacent divided by the hypotenuse".
*''TOA'' : ''t''an(theta) = ''o''pp / ''a''dj - "The //tangent// of theta is the opposite divided by the adjacent".
Calculate radians from degrees:
{{{
import math
radians = (degrees * math.pi) / 180
}}}
Calculate degrees from radians:
{{{
import math
degrees = (radians * 180) / math.pi
}}}
To see this in action:
*[[Calculating position based on angle and speed]]
----
''atan2'':
http://en.wikipedia.org/wiki/Atan2
http://docs.python.org/library/math.html#math.atan2
{{{atan2}}} is useful for determining how much something needs to be rotated to 'aim at a point'.
From wikipedia:
>In trigonometry, the two-argument function atan2 is a variation of the arctangent function. For any real arguments x and y not both equal to zero, atan2(y, x) is the angle in radians between the positive x-axis of a plane and the point given by the coordinates (x, y) on it. The angle is positive for counter-clockwise angles (upper half-plane, y > 0), and negative for clockwise angles (lower half-plane, y < 0).
Note that the arguments are atan2(''y'', ''x'') (y is before the x).
Given two points, compute how much a line aligned to the +x axis of {{{p1}}} would need to be rotated to point at {{{p2}}}:
{{{
import math
p1 = (1,1)
p2 = (3,2)
delta = (p2[0]-p1[0], p2[1]-p1[1])
radians = math.atan2(delta[1], delta[0])
degrees = (radians * 180) / math.pi
print degrees
# 26.5650511771
}}}
/***
Description: Contains the stuff you need to use Tiddlyspot
Note, you also need UploadPlugin, PasswordOptionPlugin and LoadRemoteFileThroughProxy
from http://tiddlywiki.bidix.info for a complete working Tiddlyspot site.
***/
//{{{
// edit this if you are migrating sites or retrofitting an existing TW
config.tiddlyspotSiteId = 'pygamewiki';
// make it so you can by default see edit controls via http
config.options.chkHttpReadOnly = false;
window.readOnly = false; // make sure of it (for tw 2.2)
window.showBackstage = true; // show backstage too
// disable autosave in d3
if (window.location.protocol != "file:")
config.options.chkGTDLazyAutoSave = false;
// tweak shadow tiddlers to add upload button, password entry box etc
with (config.shadowTiddlers) {
SiteUrl = 'http://'+config.tiddlyspotSiteId+'.tiddlyspot.com';
SideBarOptions = SideBarOptions.replace(/(<<saveChanges>>)/,"$1<<tiddler TspotSidebar>>");
OptionsPanel = OptionsPanel.replace(/^/,"<<tiddler TspotOptions>>");
DefaultTiddlers = DefaultTiddlers.replace(/^/,"[[WelcomeToTiddlyspot]] ");
MainMenu = MainMenu.replace(/^/,"[[WelcomeToTiddlyspot]] ");
}
// create some shadow tiddler content
merge(config.shadowTiddlers,{
'WelcomeToTiddlyspot':[
"This document is a ~TiddlyWiki from tiddlyspot.com. A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //What now?// @@ Before you can save any changes, you need to enter your password in the form below. Then configure privacy and other site settings at your [[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]] (your control panel username is //" + config.tiddlyspotSiteId + "//).",
"<<tiddler TspotControls>>",
"See also GettingStarted.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Working online// @@ You can edit this ~TiddlyWiki right now, and save your changes using the \"save to web\" button in the column on the right.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// @@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick. You can make changes and save them locally without being connected to the Internet. When you're ready to sync up again, just click \"upload\" and your ~TiddlyWiki will be saved back to tiddlyspot.com.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Help!// @@ Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]]. Also visit [[TiddlyWiki.org|http://tiddlywiki.org]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]], which is an excellent place to ask questions and get help. If you have a tiddlyspot related problem email [[tiddlyspot support|mailto:support@tiddlyspot.com]].",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// @@ We hope you like using your tiddlyspot.com site. Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments or suggestions."
].join("\n"),
'TspotControls':[
"| tiddlyspot password:|<<option pasUploadPassword>>|",
"| site management:|<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . . " + config.tiddlyspotSiteId + ">>//(requires tiddlyspot password)//<br>[[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]], [[download (go offline)|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download]]|",
"| links:|[[tiddlyspot.com|http://tiddlyspot.com/]], [[FAQs|http://faq.tiddlyspot.com/]], [[blog|http://tiddlyspot.blogspot.com/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|"
].join("\n"),
'TspotSidebar':[
"<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . . " + config.tiddlyspotSiteId + ">><html><a href='http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download' class='button'>download</a></html>"
].join("\n"),
'TspotOptions':[
"tiddlyspot password:",
"<<option pasUploadPassword>>",
""
].join("\n")
});
//}}}
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 04/01/2011 08:47:03 | WarpCat | [[/|http://pygamewiki.tiddlyspot.com/]] | [[store.cgi|http://pygamewiki.tiddlyspot.com/store.cgi]] | . | [[index.html | http://pygamewiki.tiddlyspot.com/index.html]] | . |
| 30/01/2011 19:58:41 | WarpCat | [[/|http://pygamewiki.tiddlyspot.com/]] | [[store.cgi|http://pygamewiki.tiddlyspot.com/store.cgi]] | . | [[index.html | http://pygamewiki.tiddlyspot.com/index.html]] | . | ok |
| 30/01/2011 20:01:45 | WarpCat | [[/|http://pygamewiki.tiddlyspot.com/]] | [[store.cgi|http://pygamewiki.tiddlyspot.com/store.cgi]] | . | [[index.html | http://pygamewiki.tiddlyspot.com/index.html]] | . |
| 31/01/2011 09:44:38 | WarpCat | [[/|http://pygamewiki.tiddlyspot.com/]] | [[store.cgi|http://pygamewiki.tiddlyspot.com/store.cgi]] | . | [[index.html | http://pygamewiki.tiddlyspot.com/index.html]] | . |
| 20/04/2011 09:28:19 | WarpCat | [[/|http://pygamewiki.tiddlyspot.com/]] | [[store.cgi|http://pygamewiki.tiddlyspot.com/store.cgi]] | . | [[index.html | http://pygamewiki.tiddlyspot.com/index.html]] | . |
| 29/07/2011 14:36:16 | WarpCat | [[/|http://pygamewiki.tiddlyspot.com/]] | [[store.cgi|http://pygamewiki.tiddlyspot.com/store.cgi]] | . | [[index.html | http://pygamewiki.tiddlyspot.com/index.html]] | . |
| 07/10/2011 13:57:49 | WarpCat | [[/|http://pygamewiki.tiddlyspot.com/]] | [[store.cgi|http://pygamewiki.tiddlyspot.com/store.cgi]] | . | [[index.html | http://pygamewiki.tiddlyspot.com/index.html]] | . |
| 07/10/2011 13:58:53 | WarpCat | [[/|http://pygamewiki.tiddlyspot.com/]] | [[store.cgi|http://pygamewiki.tiddlyspot.com/store.cgi]] | . | [[index.html | http://pygamewiki.tiddlyspot.com/index.html]] | . |
| 13/02/2012 16:11:50 | WarpCat | [[/|http://pygamewiki.tiddlyspot.com/]] | [[store.cgi|http://pygamewiki.tiddlyspot.com/store.cgi]] | . | [[index.html | http://pygamewiki.tiddlyspot.com/index.html]] | . |
| 13/02/2012 16:12:00 | WarpCat | [[/|http://pygamewiki.tiddlyspot.com/]] | [[store.cgi|http://pygamewiki.tiddlyspot.com/store.cgi]] | . | [[index.html | http://pygamewiki.tiddlyspot.com/index.html]] | . |
/***
|''Name:''|UploadPlugin|
|''Description:''|Save to web a TiddlyWiki|
|''Version:''|4.1.3|
|''Date:''|Feb 24, 2008|
|''Source:''|http://tiddlywiki.bidix.info/#UploadPlugin|
|''Documentation:''|http://tiddlywiki.bidix.info/#UploadPluginDoc|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
|''Requires:''|PasswordOptionPlugin|
***/
//{{{
version.extensions.UploadPlugin = {
major: 4, minor: 1, revision: 3,
date: new Date("Feb 24, 2008"),
source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
author: 'BidiX (BidiX (at) bidix (dot) info',
coreVersion: '2.2.0'
};
//
// Environment
//
if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false; // true to activate both in Plugin and UploadService
//
// Upload Macro
//
config.macros.upload = {
// default values
defaultBackupDir: '', //no backup
defaultStoreScript: "store.php",
defaultToFilename: "index.html",
defaultUploadDir: ".",
authenticateUser: true // UploadService Authenticate User
};
config.macros.upload.label = {
promptOption: "Save and Upload this TiddlyWiki with UploadOptions",
promptParamMacro: "Save and Upload this TiddlyWiki in %0",
saveLabel: "save to web",
saveToDisk: "save to disk",
uploadLabel: "upload"
};
config.macros.upload.messages = {
noStoreUrl: "No store URL in parmeters or options",
usernameOrPasswordMissing: "Username or password missing"
};
config.macros.upload.handler = function(place,macroName,params) {
if (readOnly)
return;
var label;
if (document.location.toString().substr(0,4) == "http")
label = this.label.saveLabel;
else
label = this.label.uploadLabel;
var prompt;
if (params[0]) {
prompt = this.label.promptParamMacro.toString().format([this.destFile(params[0],
(params[1] ? params[1]:bidix.basename(window.location.toString())), params[3])]);
} else {
prompt = this.label.promptOption;
}
createTiddlyButton(place, label, prompt, function() {config.macros.upload.action(params);}, null, null, this.accessKey);
};
config.macros.upload.action = function(params)
{
// for missing macro parameter set value from options
if (!params) params = {};
var storeUrl = params[0] ? params[0] : config.options.txtUploadStoreUrl;
var toFilename = params[1] ? params[1] : config.options.txtUploadFilename;
var backupDir = params[2] ? params[2] : config.options.txtUploadBackupDir;
var uploadDir = params[3] ? params[3] : config.options.txtUploadDir;
var username = params[4] ? params[4] : config.options.txtUploadUserName;
var password = config.options.pasUploadPassword; // for security reason no password as macro parameter
// for still missing parameter set default value
if ((!storeUrl) && (document.location.toString().substr(0,4) == "http"))
storeUrl = bidix.dirname(document.location.toString())+'/'+config.macros.upload.defaultStoreScript;
if (storeUrl.substr(0,4) != "http")
storeUrl = bidix.dirname(document.location.toString()) +'/'+ storeUrl;
if (!toFilename)
toFilename = bidix.basename(window.location.toString());
if (!toFilename)
toFilename = config.macros.upload.defaultToFilename;
if (!uploadDir)
uploadDir = config.macros.upload.defaultUploadDir;
if (!backupDir)
backupDir = config.macros.upload.defaultBackupDir;
// report error if still missing
if (!storeUrl) {
alert(config.macros.upload.messages.noStoreUrl);
clearMessage();
return false;
}
if (config.macros.upload.authenticateUser && (!username || !password)) {
alert(config.macros.upload.messages.usernameOrPasswordMissing);
clearMessage();
return false;
}
bidix.upload.uploadChanges(false,null,storeUrl, toFilename, uploadDir, backupDir, username, password);
return false;
};
config.macros.upload.destFile = function(storeUrl, toFilename, uploadDir)
{
if (!storeUrl)
return null;
var dest = bidix.dirname(storeUrl);
if (uploadDir && uploadDir != '.')
dest = dest + '/' + uploadDir;
dest = dest + '/' + toFilename;
return dest;
};
//
// uploadOptions Macro
//
config.macros.uploadOptions = {
handler: function(place,macroName,params) {
var wizard = new Wizard();
wizard.createWizard(place,this.wizardTitle);
wizard.addStep(this.step1Title,this.step1Html);
var markList = wizard.getElement("markList");
var listWrapper = document.createElement("div");
markList.parentNode.insertBefore(listWrapper,markList);
wizard.setValue("listWrapper",listWrapper);
this.refreshOptions(listWrapper,false);
var uploadCaption;
if (document.location.toString().substr(0,4) == "http")
uploadCaption = config.macros.upload.label.saveLabel;
else
uploadCaption = config.macros.upload.label.uploadLabel;
wizard.setButtons([
{caption: uploadCaption, tooltip: config.macros.upload.label.promptOption,
onClick: config.macros.upload.action},
{caption: this.cancelButton, tooltip: this.cancelButtonPrompt, onClick: this.onCancel}
]);
},
options: [
"txtUploadUserName",
"pasUploadPassword",
"txtUploadStoreUrl",
"txtUploadDir",
"txtUploadFilename",
"txtUploadBackupDir",
"chkUploadLog",
"txtUploadLogMaxLine"
],
refreshOptions: function(listWrapper) {
var opts = [];
for(i=0; i<this.options.length; i++) {
var opt = {};
opts.push();
opt.option = "";
n = this.options[i];
opt.name = n;
opt.lowlight = !config.optionsDesc[n];
opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
opts.push(opt);
}
var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
for(n=0; n<opts.length; n++) {
var type = opts[n].name.substr(0,3);
var h = config.macros.option.types[type];
if (h && h.create) {
h.create(opts[n].colElements['option'],type,opts[n].name,opts[n].name,"no");
}
}
},
onCancel: function(e)
{
backstage.switchTab(null);
return false;
},
wizardTitle: "Upload with options",
step1Title: "These options are saved in cookies in your browser",
step1Html: "<input type='hidden' name='markList'></input><br>",
cancelButton: "Cancel",
cancelButtonPrompt: "Cancel prompt",
listViewTemplate: {
columns: [
{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
{name: 'Option', field: 'option', title: "Option", type: 'String'},
{name: 'Name', field: 'name', title: "Name", type: 'String'}
],
rowClasses: [
{className: 'lowlight', field: 'lowlight'}
]}
};
//
// upload functions
//
if (!bidix.upload) bidix.upload = {};
if (!bidix.upload.messages) bidix.upload.messages = {
//from saving
invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
backupSaved: "Backup saved",
backupFailed: "Failed to upload backup file",
rssSaved: "RSS feed uploaded",
rssFailed: "Failed to upload RSS feed file",
emptySaved: "Empty template uploaded",
emptyFailed: "Failed to upload empty template file",
mainSaved: "Main TiddlyWiki file uploaded",
mainFailed: "Failed to upload main TiddlyWiki file. Your changes have not been saved",
//specific upload
loadOriginalHttpPostError: "Can't get original file",
aboutToSaveOnHttpPost: 'About to upload on %0 ...',
storePhpNotFound: "The store script '%0' was not found."
};
bidix.upload.uploadChanges = function(onlyIfDirty,tiddlers,storeUrl,toFilename,uploadDir,backupDir,username,password)
{
var callback = function(status,uploadParams,original,url,xhr) {
if (!status) {
displayMessage(bidix.upload.messages.loadOriginalHttpPostError);
return;
}
if (bidix.debugMode)
alert(original.substr(0,500)+"\n...");
// Locate the storeArea div's
var posDiv = locateStoreArea(original);
if((posDiv[0] == -1) || (posDiv[1] == -1)) {
alert(config.messages.invalidFileError.format([localPath]));
return;
}
bidix.upload.uploadRss(uploadParams,original,posDiv);
};
if(onlyIfDirty && !store.isDirty())
return;
clearMessage();
// save on localdisk ?
if (document.location.toString().substr(0,4) == "file") {
var path = document.location.toString();
var localPath = getLocalPath(path);
saveChanges();
}
// get original
var uploadParams = new Array(storeUrl,toFilename,uploadDir,backupDir,username,password);
var originalPath = document.location.toString();
// If url is a directory : add index.html
if (originalPath.charAt(originalPath.length-1) == "/")
originalPath = originalPath + "index.html";
var dest = config.macros.upload.destFile(storeUrl,toFilename,uploadDir);
var log = new bidix.UploadLog();
log.startUpload(storeUrl, dest, uploadDir, backupDir);
displayMessage(bidix.upload.messages.aboutToSaveOnHttpPost.format([dest]));
if (bidix.debugMode)
alert("about to execute Http - GET on "+originalPath);
var r = doHttp("GET",originalPath,null,null,username,password,callback,uploadParams,null);
if (typeof r == "string")
displayMessage(r);
return r;
};
bidix.upload.uploadRss = function(uploadParams,original,posDiv)
{
var callback = function(status,params,responseText,url,xhr) {
if(status) {
var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
displayMessage(bidix.upload.messages.rssSaved,bidix.dirname(url)+'/'+destfile);
bidix.upload.uploadMain(params[0],params[1],params[2]);
} else {
displayMessage(bidix.upload.messages.rssFailed);
}
};
// do uploadRss
if(config.options.chkGenerateAnRssFeed) {
var rssPath = uploadParams[1].substr(0,uploadParams[1].lastIndexOf(".")) + ".xml";
var rssUploadParams = new Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
var rssString = generateRss();
// no UnicodeToUTF8 conversion needed when location is "file" !!!
if (document.location.toString().substr(0,4) != "file")
rssString = convertUnicodeToUTF8(rssString);
bidix.upload.httpUpload(rssUploadParams,rssString,callback,Array(uploadParams,original,posDiv));
} else {
bidix.upload.uploadMain(uploadParams,original,posDiv);
}
};
bidix.upload.uploadMain = function(uploadParams,original,posDiv)
{
var callback = function(status,params,responseText,url,xhr) {
var log = new bidix.UploadLog();
if(status) {
// if backupDir specified
if ((params[3]) && (responseText.indexOf("backupfile:") > -1)) {
var backupfile = responseText.substring(responseText.indexOf("backupfile:")+11,responseText.indexOf("\n", responseText.indexOf("backupfile:")));
displayMessage(bidix.upload.messages.backupSaved,bidix.dirname(url)+'/'+backupfile);
}
var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
displayMessage(bidix.upload.messages.mainSaved,bidix.dirname(url)+'/'+destfile);
store.setDirty(false);
log.endUpload("ok");
} else {
alert(bidix.upload.messages.mainFailed);
displayMessage(bidix.upload.messages.mainFailed);
log.endUpload("failed");
}
};
// do uploadMain
var revised = bidix.upload.updateOriginal(original,posDiv);
bidix.upload.httpUpload(uploadParams,revised,callback,uploadParams);
};
bidix.upload.httpUpload = function(uploadParams,data,callback,params)
{
var localCallback = function(status,params,responseText,url,xhr) {
url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
if (xhr.status == 404)
alert(bidix.upload.messages.storePhpNotFound.format([url]));
if ((bidix.debugMode) || (responseText.indexOf("Debug mode") >= 0 )) {
alert(responseText);
if (responseText.indexOf("Debug mode") >= 0 )
responseText = responseText.substring(responseText.indexOf("\n\n")+2);
} else if (responseText.charAt(0) != '0')
alert(responseText);
if (responseText.charAt(0) != '0')
status = null;
callback(status,params,responseText,url,xhr);
};
// do httpUpload
var boundary = "---------------------------"+"AaB03x";
var uploadFormName = "UploadPlugin";
// compose headers data
var sheader = "";
sheader += "--" + boundary + "\r\nContent-disposition: form-data; name=\"";
sheader += uploadFormName +"\"\r\n\r\n";
sheader += "backupDir="+uploadParams[3] +
";user=" + uploadParams[4] +
";password=" + uploadParams[5] +
";uploaddir=" + uploadParams[2];
if (bidix.debugMode)
sheader += ";debug=1";
sheader += ";;\r\n";
sheader += "\r\n" + "--" + boundary + "\r\n";
sheader += "Content-disposition: form-data; name=\"userfile\"; filename=\""+uploadParams[1]+"\"\r\n";
sheader += "Content-Type: text/html;charset=UTF-8" + "\r\n";
sheader += "Content-Length: " + data.length + "\r\n\r\n";
// compose trailer data
var strailer = new String();
strailer = "\r\n--" + boundary + "--\r\n";
data = sheader + data + strailer;
if (bidix.debugMode) alert("about to execute Http - POST on "+uploadParams[0]+"\n with \n"+data.substr(0,500)+ " ... ");
var r = doHttp("POST",uploadParams[0],data,"multipart/form-data; ;charset=UTF-8; boundary="+boundary,uploadParams[4],uploadParams[5],localCallback,params,null);
if (typeof r == "string")
displayMessage(r);
return r;
};
// same as Saving's updateOriginal but without convertUnicodeToUTF8 calls
bidix.upload.updateOriginal = function(original, posDiv)
{
if (!posDiv)
posDiv = locateStoreArea(original);
if((posDiv[0] == -1) || (posDiv[1] == -1)) {
alert(config.messages.invalidFileError.format([localPath]));
return;
}
var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
store.allTiddlersAsHtml() + "\n" +
original.substr(posDiv[1]);
var newSiteTitle = getPageTitle().htmlEncode();
revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
return revised;
};
//
// UploadLog
//
// config.options.chkUploadLog :
// false : no logging
// true : logging
// config.options.txtUploadLogMaxLine :
// -1 : no limit
// 0 : no Log lines but UploadLog is still in place
// n : the last n lines are only kept
// NaN : no limit (-1)
bidix.UploadLog = function() {
if (!config.options.chkUploadLog)
return; // this.tiddler = null
this.tiddler = store.getTiddler("UploadLog");
if (!this.tiddler) {
this.tiddler = new Tiddler();
this.tiddler.title = "UploadLog";
this.tiddler.text = "| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |";
this.tiddler.created = new Date();
this.tiddler.modifier = config.options.txtUserName;
this.tiddler.modified = new Date();
store.addTiddler(this.tiddler);
}
return this;
};
bidix.UploadLog.prototype.addText = function(text) {
if (!this.tiddler)
return;
// retrieve maxLine when we need it
var maxLine = parseInt(config.options.txtUploadLogMaxLine,10);
if (isNaN(maxLine))
maxLine = -1;
// add text
if (maxLine != 0)
this.tiddler.text = this.tiddler.text + text;
// Trunck to maxLine
if (maxLine >= 0) {
var textArray = this.tiddler.text.split('\n');
if (textArray.length > maxLine + 1)
textArray.splice(1,textArray.length-1-maxLine);
this.tiddler.text = textArray.join('\n');
}
// update tiddler fields
this.tiddler.modifier = config.options.txtUserName;
this.tiddler.modified = new Date();
store.addTiddler(this.tiddler);
// refresh and notifiy for immediate update
story.refreshTiddler(this.tiddler.title);
store.notify(this.tiddler.title, true);
};
bidix.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir, backupDir) {
if (!this.tiddler)
return;
var now = new Date();
var text = "\n| ";
var filename = bidix.basename(document.location.toString());
if (!filename) filename = '/';
text += now.formatString("0DD/0MM/YYYY 0hh:0mm:0ss") +" | ";
text += config.options.txtUserName + " | ";
text += "[["+filename+"|"+location + "]] |";
text += " [[" + bidix.basename(storeUrl) + "|" + storeUrl + "]] | ";
text += uploadDir + " | ";
text += "[[" + bidix.basename(toFilename) + " | " +toFilename + "]] | ";
text += backupDir + " |";
this.addText(text);
};
bidix.UploadLog.prototype.endUpload = function(status) {
if (!this.tiddler)
return;
this.addText(" "+status+" |");
};
//
// Utilities
//
bidix.checkPlugin = function(plugin, major, minor, revision) {
var ext = version.extensions[plugin];
if (!
(ext &&
((ext.major > major) ||
((ext.major == major) && (ext.minor > minor)) ||
((ext.major == major) && (ext.minor == minor) && (ext.revision >= revision))))) {
// write error in PluginManager
if (pluginInfo)
pluginInfo.log.push("Requires " + plugin + " " + major + "." + minor + "." + revision);
eval(plugin); // generate an error : "Error: ReferenceError: xxxx is not defined"
}
};
bidix.dirname = function(filePath) {
if (!filePath)
return;
var lastpos;
if ((lastpos = filePath.lastIndexOf("/")) != -1) {
return filePath.substring(0, lastpos);
} else {
return filePath.substring(0, filePath.lastIndexOf("\\"));
}
};
bidix.basename = function(filePath) {
if (!filePath)
return;
var lastpos;
if ((lastpos = filePath.lastIndexOf("#")) != -1)
filePath = filePath.substring(0, lastpos);
if ((lastpos = filePath.lastIndexOf("/")) != -1) {
return filePath.substring(lastpos + 1);
} else
return filePath.substring(filePath.lastIndexOf("\\")+1);
};
bidix.initOption = function(name,value) {
if (!config.options[name])
config.options[name] = value;
};
//
// Initializations
//
// require PasswordOptionPlugin 1.0.1 or better
bidix.checkPlugin("PasswordOptionPlugin", 1, 0, 1);
// styleSheet
setStylesheet('.txtUploadStoreUrl, .txtUploadBackupDir, .txtUploadDir {width: 22em;}',"uploadPluginStyles");
//optionsDesc
merge(config.optionsDesc,{
txtUploadStoreUrl: "Url of the UploadService script (default: store.php)",
txtUploadFilename: "Filename of the uploaded file (default: in index.html)",
txtUploadDir: "Relative Directory where to store the file (default: . (downloadService directory))",
txtUploadBackupDir: "Relative Directory where to backup the file. If empty no backup. (default: ''(empty))",
txtUploadUserName: "Upload Username",
pasUploadPassword: "Upload Password",
chkUploadLog: "do Logging in UploadLog (default: true)",
txtUploadLogMaxLine: "Maximum of lines in UploadLog (default: 10)"
});
// Options Initializations
bidix.initOption('txtUploadStoreUrl','');
bidix.initOption('txtUploadFilename','');
bidix.initOption('txtUploadDir','');
bidix.initOption('txtUploadBackupDir','');
bidix.initOption('txtUploadUserName','');
bidix.initOption('pasUploadPassword','');
bidix.initOption('chkUploadLog',true);
bidix.initOption('txtUploadLogMaxLine','10');
// Backstage
merge(config.tasks,{
uploadOptions: {text: "upload", tooltip: "Change UploadOptions and Upload", content: '<<uploadOptions>>'}
});
config.backstageTasks.push("uploadOptions");
//}}}
PyGame doesn't have a vector lib, nor does Python. Pain. But there are options out there:
*http://pygame.org/wiki/2DVectorClass
*http://pygame.org/wiki/3DVectorClass
http://en.wikipedia.org/wiki/Euclidean_vector
http://mathforum.org/~klotz/Vectors/vectors.html
http://mathworld.wolfram.com/Vector.html
http://mathworld.wolfram.com/topics/VectorAlgebra.html
http://www.geocities.com/SiliconValley/2151/math3d.html
Forum post, and example:
http://forums.overclockers.com.au/showthread.php?t=870396
http://dl.dropbox.com/u/3240460/cloth5.py
http://codeflow.org/entries/2010/nov/29/verlet-collision-with-impulse-preservation/
http://codeflow.org/entries/2010/sep/01/hard-constraints-easy-solutions/
http://codeflow.org/entries/2010/aug/28/integration-by-example-euler-vs-verlet-vs-runge-kutta/
----
http://www.gpgstudy.com/gpgiki/GDC%202001%3A%20Advanced%20Character%20Physics
<<gradient horiz #ffffff #ddddff #8888ff>>
[img[warpcat|http://farm3.static.flickr.com/2017/2118148943_75636dd96c.jpg?v=0]] Yes, that is me playing [[Rock Band|http://www.rockband.com/]].
''Eric Pavey''
*Email: - warpcat {{{(at)}}} sbcglobal {{{(dot)}}} net
*Technical Art Director in the video games industry.
*[[Blog|http://www.akeric.com/blog/]] - [[LinkedIn|http://www.linkedin.com/in/pavey]] - [[Flickr|http://www.flickr.com/photos/8064698@N03/collections/]] - [[Youtube|http://www.youtube.com/profile?user=warpcat]] - [[MobyGames|http://www.mobygames.com/developer/sheet/view/developerId,76979/]] - [[RSWarrior|http://rswarrior.com/photos/Warpcat/default.aspx]]
>>
My other Tiddlywiki's:
*[[Python wiki|http://pythonwiki.tiddlyspot.com/]]
*[[mel wiki|http://mayamel.tiddlyspot.com/]]
*[[Processing wiki|http://processingwiki.tiddlyspot.com/]]
*[[CG OpenSource wiki|http://cgoswiki.tiddlyspot.com/]]
<<gradient horiz #ffffff #ddddff #8888ff >>
[[About PyGame Wiki]] (go here first)
[[Instructions For Use]] (go here second)
[[Check out the latest updates|History]] (go here third. //Hit ''F5'' to refresh your cache to see latest stuff.//)
<<gradient horiz #ddddff #8888ff >>''Browse \ Search using:''
----
{{{<---}}} Major ''//Categories//'' (or 'All Subjects') in the Left column
Key-word ''//Tags//'' tab in the Right column {{{--->}}}
The ''//Search//'' box in the Right column (top) FYI, this can slow down the wiki. {{{--->}}}
----
>>
Best viewed in ''[[Firefox|http://www.mozilla.com]]''
Running [[tiddlywiki|http://www.tiddlywiki.com]] v<<version>>
If you find this wiki useful, let the [[author|WarpCat]] know!
Also check out my [[Python Wiki|http://pythonwiki.tiddlyspot.com/]]
>>
http://www.pygame.org/docs/ref/mixer.html
Audio in PyGame is handled through it's {{{pygame.mixer}}} module. From the docs:
>This module contains classes for loading Sound objects and controlling playback. The mixer module is optional and depends on ~SDL_mixer. Your program should test that pygame.mixer is available and intialized before using it.
Example function to load audio:
{{{
def sound_load(path):
"""
Modified version from the monkey PyGame tutorial
path : relative path to the sound file
return : Sound
"""
class NoneSound:
def play(self): pass
if not pygame.mixer or not pygame.mixer.get_init():
return NoneSound()
try:
sound = pygame.mixer.Sound(path)
except pygame.error, message:
print 'Cannot load sound:', path
raise SystemExit, message
return sound
}}}
Pygame has a {{{color}}} //module//, and {{{Color}}} //objects//:
http://www.pygame.org/docs/ref/color.html - link to {{{Color}}} //objects//.
----
There is a common technique to 'import PyGame locals' at the start of a program, like this:
{{{
from pygame.locals import *
}}}
What does this mean for color? These are the lines of code that relate to color:
{{{
import pygame.color as color
Color = color.Color
}}}
So this means you can acess anything in {{{pygame.color}}} simply via {{{color}}}, and it means you can access {{{Color}}} objects directly.
Presuming you have imported locals, you can access color data in these ways:
{{{
import pygame
from pygame.locals import *
print Color("red")
print pygame.Color("red")
print pygame.color.Color("red")
(255, 0, 0, 255)
(255, 0, 0, 255)
(255, 0, 0, 255)
}}}
If you //haven't// imported locals, you'd need to access all your color via this method:
{{{
import pygame
print pygame.Color("red")
print pygame.color.Color("red")
(255, 0, 0, 255)
(255, 0, 0, 255)
}}}
{{{pygame.color}}} is a //module// (I uh... think...) which holds the class {{{Color}}} and the dictionary {{{THECOLORS}}}.
----
{{{pygame.color}}} has a {{{dictionary}}} constant called {{{THECOLORS}}} which can be imported, and used directly for //named colors//:
{{{
from pygame.color import THECOLORS
print THECOLORS["yellow"]
# (255, 255, 0, 255)
}}}
The //key// of the dictionary is the name of the color in question, and the resultant value is a tuple of four ints, for R, G, B, A
To get a list of all the colors:
{{{
for k in sorted(THECOLORS):
print k, THECOLORS[k]
# prints:
aliceblue (240, 248, 255, 255)
antiquewhite (250, 235, 215, 255)
antiquewhite1 (255, 239, 219, 255)
antiquewhite2 (238, 223, 204, 255)
antiquewhite3 (205, 192, 176, 255)
antiquewhite4 (139, 131, 120, 255)
aquamarine (127, 255, 212, 255)
# etc...
}}}
It should be noted that {{{THECOLORS}}} actually lives in its own module:
{{{
\pygame\colordict.py
}}}
I can only presume that during PyGame startup, this module is imported into {{{pygame.color}}}
----
Making your own colors is really easy:
{{{
# locals are imported...
blurple = Color(128, 0, 255)
print blurple
# (128, 0, 255, 255)
}}}
http://www.pygame.org/docs/ref/display.html
>"Pygame has a single display [[Surface]] that is either contained in a window or runs full screen. Once you create the display you treat it as a regular Surface. Changes are not immediately visible onscreen, you must choose one of the two flipping functions to update the actual display. "
So, running the below {{{display}}} code will create the main display, which is a {{{Surface}}}:
{{{
screen = pygame.display.set_mode(args & flags in here...)
}}}
Once the Surface has been created (or during its creation), these flags are available for {{{pygame.display.set_mode()}}} - initialize a window or screen for display:
*{{{pygame.FULLSCREEN}}} create a fullscreen display
*{{{pygame.DOUBLEBUF}}} recommended for HWSURFACE or OPENGL. Generates two displays to draw for faster drawing, implemented with {{{display.flip()}}} rather than {{{display.update}}}.
*{{{pygame.HWSURFACE}}} hardware accelerated, only in FULLSCREEN
*{{{pygame.OPENGL}}} create an opengl renderable display
*{{{pygame.RESIZABLE}}} display window should be sizeable
*{{{pygame.NOFRAME}}} display window will have no border or controls
----
There seem to be two main ways to refresh the display: {{{display.flip()}}} and {{{display.update()}}}:
*{{{display.flip()}}} should be used when using both '{{{HWSURFACE}}}' and '{{{DOUBLEBUF}}}' draw modes, since it instantly 'flips' one of the double-buffered //hardware// displays to the next. This mode can still be used in software draw mode as well.
**http://www.pygame.org/docs/ref/display.html#pygame.display.flip
*{{{display.update()}}} should be used otherwise. It: "...is like an optimized version of {{{display.flip}}} - for //software// displays. It allows only a portion of the screen to updated, instead of the entire area. If no argument is passed it updates the entire Surface area like {{{display.flip}}}"
**http://www.pygame.org/docs/ref/display.html#pygame.display.update
----
Concepts:
*{{{pygame.display}}} is //the screen// you're looking at.
*This 'screen' happens to be a {{{pygame.Surface}}} object. There's a couple different ways to make this object
{{{
# make an empty Surface object based on the width and height of the 'screen'
screenSurf = pygame.display.set_mode(width, height)
# Method A: load an image for the background
# (should be the same width and height as our screen)
bgSurf = pygame.image.load(pathToImage)
bgSurf = bgSurf .convert()
# Method B: Make a new surface filled with a color as the background:
bgSurf = pygame.Surface(screenSurf.get_size())
bgSurf = bgSurf.convert()
# Fill with green:
bgSurf.fill((0, 255, 0))
# blit our background to our screen:
screenSurf.blit(bgSurf, (0,0))
while True:
# Do a bunch of other pygame drawing stuff....
# Update our display once per frame:
pygame.display.flip()
# or
pygame.display.update()
}}}
In the above example, the background is only blitted once to the screen. When you blit one surface to another, it physically changes that surface... it sort of layers the blitted surface on top of the original surface, and saved the original surface that way.
If you were updating graphics to the background on every frame, you'd need to move its blitting into the main loop:
{{{
while True:
# Do a bunch of other pygame drawing stuff that blit to
# the background...
# blit our background to our screen, thus reflecting all the
# previous changes to it:
screenSurf.blit(bgSurf, (0,0))
# Update our display once per frame:
pygame.display.flip()
# or
pygame.display.update()
}}}
http://www.pygame.org/docs/ref/font.html
!Simple overview:
First, you make a {{{Font}}} object. Really only need to do this once per font type \ size \ bold \ italic style:
http://www.pygame.org/docs/ref/font.html#pygame.font.SysFont
http://www.pygame.org/docs/ref/font.html#pygame.font.Font
{{{pygame.font.SysFont(name, size, bold=False, italic=False)}}}
{{{pygame.font.Font(filename, size)}}}
{{{pygame.font.Font(object, size)}}}
{{{
# using SysFonts can be dangerous for distribution
myFont = pygame.font.SysFont("arial", 16)
# presuming that Couier.ttf lives in the same folder:
myFont = pygame.font.Font("Couier.ttf", 16)
# You can also pass in 'None' to use the default PyGame font:
myFont = pygame.font.Font(None, 16)
}}}
Then you create one or more [[surfaces|Surface]] based on that font to blit:
http://www.pygame.org/docs/ref/font.html#Font.render
{{{
labelSurf = myFont.render("font text", 1, Color("white"))
}}}
Finally, you'd blit that to your screen\background like any other surface:
{{{
screen.blit(labelSurf, screenRect.center)
}}}
!Other stuff
To get a list of fonts on //your system//. However, these may not exist on other systems.
{{{
print sorted(pygame.font.get_fonts())
# ['academy', 'academyengravedletplain10', 'alba', 'albamatter', ....
}}}
----
Fonts seem to be a pain when making executables. Presuming you're using the default pygame font, you'll need to make sure your {{{setup.py}}} file points to these additional items:
*{{{SDL_ttf.dll}}}
*{{{zlib1.dll}}}
*{{{libfreetype-6.dll}}}
*{{{pygame.font.get_default_font()}}} (only if you're using the default font)
If you're using a custom {{{ttf}}} font, you'll need to add it to the dir you're making the executable from, and be sure to add its name to the file list.
Info and tutorials on physics:
*http://www.gamedev.net/reference/programming/features/2dcarphys/
*http://stackoverflow.com/questions/345838/ball-to-ball-collision-detection-and-handling
*http://stackoverflow.com/questions/780169/how-do-i-create-collision-detections-for-my-bouncing-balls
*http://www.euclideanspace.com/threed/games/examples/snooker/index.htm
*http://mathforum.org/~klotz/Vectors/vectors.html
*http://compsci.ca/v3/viewtopic.php?t=17591
*http://en.wikipedia.org/wiki/Elastic_collision
*http://us.metamath.org/symbols/symbols.html
*http://www.doe.virginia.gov/Div/Winchester/jhhs/math/facts/symbol.html
*http://wiki.laptop.org/go/Python_Physics
*http://hyperphysics.phy-astr.gsu.edu/hbase/traj.html
*http://www.makingthemodernworld.org/learning_modules/maths/04.TU.02/?section=1
*http://codeflow.org/entries/2010/sep/01/hard-constraints-easy-solutions/
*http://codeflow.org/entries/2010/nov/29/verlet-collision-with-impulse-preservation/
http://www.pygame.org/docs/ref/surface.html
http://www.pygame.org/docs/ref/draw.html
I've been working on a 'paint' program using 2d physics via the [[PyMunk]] physics engine. You paint little "circles" on the screen with the mouse. I've tried two different methods:
#Using {{{pygame.draw.circle}}} to render them to the screen
#Pre-generating {{{pygame.Surface}}} objects (built as Sprites), filling them with circles, and drawing them.
Surprising to me, the {{{draw.circle}}} method was exponentially faster:
*Drawing a single 'circle-Surface' to the screen cut my framerate from 54fps to 23fps.
*But, I can nearly fill the screen with the same sized circles (roughly 170) via the draw call, and it's just then hitting the low 20's.
I tried combination of both hardware acceleration (using {{{display.flip()}}}, including modifying how I built my surfaces to support hardware transparency) and regular flavor ({{{display.update()}}} with rect-blitting only), and the stats really didn't change that much.
I'd have figured that needing to custom-draw each circle every frame would take a lot more cycles than just blitting a pre-compted image, but currently the results say otherwise.
http://www.pygame.org/docs/ref/sprite.html
Good tutorial:
http://www.sacredchao.net/~piman/writing/sprite-tutorial.shtml
>"This module ({{{pygame.sprite}}}) contains several simple classes to be used within games. There is the main {{{pygame.sprite.Sprite}}} class and several {{{pygame.sprite.Group}}} classes that contain {{{Sprite}}}s. The use of these classes is entirely optional when using Pygame. The classes are fairly lightweight and only provide a starting place for the code that is common to most games."
>"The {{{pygame.sprite.Sprite}}} //class// is intended to be used as a base class for the different types of objects in the game. There is also a base Group class that simply stores sprites. A game could create new types of Group classes that operate on specially customized Sprite instances they contain."
----
*@@Conceptually@@:
**You create ''Sprites'' to save position ({{{self.rect}}} : [[Rect]]) and image ({{{self.image}}} : [[Surface]]) information, and do work ({{{self.update()}}}). They themselves don't (shouldn't, anything is possible) draw\blit themselves to the screen. Their {{{self.image}}} & {{{self.rect}}} attrs, and {{{self.update()}}} methods are the only ones required to be used in a "sprite group".
**You create sprite ''Groups'' to hold like-minded sprites, and draw\blit them to the screen, and handle sprite collisions.
----
''SPRITES''
*http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Sprite
*http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.DirtySprite
*"A "sprite", at least in Pygame terms, is an object that contains both an image (a {{{Surface}}}), and a location at which to draw that image (a {{{Rect}}})."
*Sprite objects have two important //instance variables// (used by sprite groups):
**{{{self.image}}} : a [[Surface]], which is the current image that will be displayed.
**{{{self.rect}}} : a [[Rect]], the location at which this image will be displayed when the sprite is drawn to the screen.
*Sprites also have one important //instance method// (used by sprite groups):
**{{{self.update()}}}. This is called too via the enclosing sprite Groups {{{Group.update()}}} method.
Here is an example of a very basic sprite, that will follow the mouse position around:
{{{
class NullSprite(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
# setup required image and rect attrs:
self.image = pygame.surface.Surface((32,32))
self.rect = self.image.fill((64,64,64))
self.x = 0
self.y = 0
def update(self):
# this method is called to via sprite groups
self.rect.center = map(lambda x, y: x-y, pygame.mouse.get_pos(), (self.x, self.y))
}}}
For a given Sprite {{{sprite}}}, //blit// it to the main screen. This can be done as an alternative to using sprite groups. But sprite groups appear to be the preferred method.
{{{
screen.blit(sprite.image, sprite.rect)
}}}
''Important'':
It should be noted that a Sprite's position is usually stored in {{{self.x}}} and {{{self.y}}} attrs (names are arbitrary), which are (should be) {{{float}}} values. This lets us maintain precision. This is in comparison to storing the data in the {{{self.rect.center}}}, which is a tuple of //integers//, that can loose precision with small values. Conceptually, you update the {{{self.x}}} and {{{self.y}}} via the Sprite object's methods, and then upon completion, set the {{{self.rect.center}}} to the {{{self.x}}} and {{{self.y}}}:
{{{
def update(self):
# do lots of work
# finally:
self.rect.center = (self.x, self.y)
}}}
----
''GROUPS''
*Sprites are always dealt with in groups - even if a group only has one Sprite.
*Base {{{Group}}}: http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Group
The base {{{sprite.Group}}} has several important methods. Other sprite Group types extend these with their own.
*{{{Group.add()}}} & {{{Group.remove()}}}: And or remove a single sprite. list of sprites, or a pre-made Group of sprites to\from another group.
*{{{Group.draw()}}} Draws\blits the sprites to the given surface. It does his by accessing the individual Sprite's {{{Sprite.image}}} and {{{Sprite.rect}}} attrs. They are drawn in random order.
*{{{Group.update()}}} calls to each individual Sprite's {{{Sprite.update()}}} method. Any args passed to the Group are passed to the Sprite. It's a good idea to have the Sprites individual {{{.update()}}} methods to have the same args, so they can all be accessed the same way through the Group.
*{{{Group.sprites()}}} : Returns a list of sprites. It's also an iterator in PyGame 1.7 and later, so you can do {{{for sprite in myGroup:}}}
*{{{Group.clear()}}} : Erases the Sprites used in the //last// (frame before) {{{Group.draw()}}} call. The destination Surface is cleared by filling the drawn Sprite positions with the background.
Sprite Group types:
*{{{sprite.Group}}} : A simple container for Sprite objects. This class can be inherited to create containers with more specific behaviors.
**http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Group
*{{{sprite.RenderUpdates}}} : This class is derived from sprite.Group. It has an extended {{{draw()}}} method that tracks the changed areas of the screen.
**http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.RenderUpdates
*{{{sprite.OrderedUpdates}}} : This class derives from {{{sprite.RenderUpdates}}}. It maintains the order in which the Sprites were added to the Group for rendering. This makes adding and removing Sprites from the Group a little slower than regular Groups.
**http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.OrderedUpdates
*{{{sprite.LayerUpdates}}} : You can set the default layer through {{{**kwargs}}} using 'default_layer' and an integer for the layer. The default layer is 0. ???
**http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.LayeredUpdates
*{{{sprite.LayeredDirty}}} : It uses the dirty flag technique and is therefore faster than the pygame.sprite.RenderUpdates if you have many static sprites. It also switches automatically between dirty rect update and full screen drawing, so you do no have to worry what would be faster.
**http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.LayeredDirty
*{{{sprite.GroupSingle}}} : The GroupSingle container only holds a single Sprite. When a new Sprite is added, the old one is removed.
**http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.GroupSingle
----
Sprite implementation, pseudo code:
{{{
#Define our screen:
screen = pygame.display.set_mode((1024, 768))
# Use the 'NullSprite' from above, and make a sprite instance:
nSprite = NullSprite()
# put into a group for rendering:
sprite_group = pygame.sprite.Group(nSprite)
# draw to screen:
sprite_group.draw(screen)
}}}
Another example: When putting sprites in groups, the below example is a general system for executing the group's methods. In the below example, we use a '{{{RenderUpdates}}}' sprite group, who's {{{draw()}}} method returns a list of rects to update via {{{pygame.display.update(rects)}}}. The base sprite group {{{sprite.Group}}} has the same method name, but it returns nothing ({{{RenderUpdates}}} is a wrapper around {{{Group}}}, and overrides that function).
{{{
sprite_group = pygame.sprite.RenderUpdates(nSprite)
# Clear the last drawn position of the sprites (drawn via the last loops
# 'sprite_group.draw() call) with the background:
sprite_group.clear(screen , background)
# For each sprite in the group, call to their update() method
# (usually to move then around):
sprite_group.update()
# blit each sprite in the group to the screen via the individual sprites
# self.image and self.rect attrs. Capture the resulting rect regions updated.
updateRects = sprite_group.draw(screen)
# re-draw our display screen, but only in the areas that the sprites currently occupy:
pygame.display.update(updateRects)
}}}
In a nutshell it goes:
*clear screen based on last position of sprites in group
*run update code for each sprite in group
*draw group to screen, capture updated rects
*update the display, but only in the updated group regions
----
http://www.pygame.org/docs/ref/time.html
To lock your game at a constant framerate. Let's use 30 fps:
{{{
clock = pygame.time.Clock()
# main loop:
while true:
clock.tick(30)
}}}
Setup motion to be relative to framerate. This will keep you assets moving the same distance relative to time, even on slower\faster machines:
{{{
clock = pygame.time.Clock()
# speed in pixels per second:
pps = 128
# main loop:
while True:
# You could optionally pass in a value to tick to set the maximum framerate.
# In this case, it will run as fast as the machine will allow...
time_passed = clock.tick()
time_passed_seconds = time_passed / 1000.0
distance_moved = time_passed_seconds * pps
someVal += distance_moded
}}}
http://www.pygame.org/docs/ref/transform.html
Transform operates on [[Surface]] objects.
Notes:
*{{{transform.rotate}}} & {{{transform.rotozoom}}} : Positive values are counterclockwise in direction.
*"Zero" degrees is at the 3:00 position.