Walkcycles and timing in pygame - pygame

I have a pygame.Timer running in my game calling a draw function 32 times/second. The drawing method gets positions from all elements on my screen and blits them accordingly. However, I want the main character to walk around slower than other objects move.
Should I set up a timer specifically for it or should I just blit the same frames several times? Is there any better way to do it? A push in the right direction would be awesome :)
(If anyone's interested, here is the code that currently controls what frames to send to the drawing: http://github.com/kallepersson/subterranean-ng/blob/master/Player.py#L88)

Your walk cycle frame (like all motion) should be a function of absolute time, not of frame count. e.g.:
def walk_frame(millis, frames_per_second, framecount, start_millis=0):
millis_per_frame = 1000 / frames_per_second
elapsed_millis = millis - start_millis
total_frames = elapsed_millis / millis_per_frame
return total_frames % framecount

Related

How to make a toggle type button in pygame?

I'm making a game with pygame, but I have a problem that button does not work as a toggle.
while True:
mousepos = pygame.mouse.get_pos()
mouseclk = pygame.mouse.get_pressed()
game.fill((0,0,0))
game.blit(cic, (0,0))
if mouseclk[0] and 1227 > mousepos[0] > 1120 and 485 > mousepos[1] > 413:
while True:
game.blit(pbut_rgm84, (1120,413))
pygame.mixer.music.load("sounds\se_beep.mp3")
pygame.mixer.music.play(0)
current_weaponinf = font.render(current_weapon[1], True, (255, 0, 0))
game.blit(current_weaponinf, (930,45))
else:
game.blit(but_rgm84, (1120,413))
This is my code for the button. As you know, If I click on that button, image of the button will be changed and sound will be played. But it will happen while i'm pressing on the button. So, I want to the button to be toggle type. Do you have any ideas on it?
EDIT 1: Sorry for the ramble. The answer is in the first part. The rest is just some notes on a more standard way to do what you asked for in PyGame. After all, the answer is not just for the OP, but everyone who visits this page.
EDIT 2: Notice also I had to change the code to lower down the FPS to 5 for this to work fine. The way you checked for a click meant that the mouse button was down for about 30 - 40 frames every click on my machine, hence resulting in 30 to 40 button toggles when only one was needed, so I had to lower down the FPS to 5 in the code. This is another reason why you might want to use the other method I described for event handling.
Answer:
I honestly appreciate the simple logic you used to check if the mouse click was in range of the object being pressed.
I could extend your code slightly to get the "toggle" type button you want (i.e, on a click, the image and sound changes. Click again, and the image and sound go back to normal).
However, I also would like to show you some better ways of dealing with mouse click events and click detection available in the Pygame module to improve your code. If you are interested and want to know what I mean, please read on after the code below. If you just wanna take the code and leave, I totally respect that (it's something I did a lot as well).
Here is your code modified to match your "toggle" button requirements (if I understood what you want to do right). Replace this with the part of your code you showed in your answer, but be aware that this may not work as I don't have your full code or the images / soundtracks you used :
toggle = False
pygame.mixer.music.load("sounds\se_beep.mp3")
pygame.mixer.music.play(-1)
pygame.mixer.music.pause()
clock = pygame.time.Clock()
while True:
mousepos = pygame.mouse.get_pos()
mouseclk = pygame.mouse.get_pressed()
game.fill((0, 0, 0))
game.blit(cic, (0, 0))
if mouseclk[0] and 1227 > mousepos[0] > 1120 and 485 > mousepos[1] > 413:
if toggle:
toggle = False
pygame.mixer.music.pause()
else:
toggle = True
pygame.mixer.music.unpause()
if toggle:
game.blit(pbut_rgm84, (1120, 413))
current_weaponinf = font.render(current_weapon[1], True, (255, 0, 0))
game.blit(current_weaponinf, (930,45))
else:
game.blit(but_rgm84, (1120,413))
clock.tick(5)
A Better Way: Handling Events
Handling mouse click events and in fact all other events is simple with what is called an event loop. Although there is a little bit more to it than this, for now we can say than an event loop checks for certain events triggered (such as key press, mouse click, quit game events) at every frame, and then triggers a callback function.
You already have one in your code! - At every loop in your while loop, you check if the mouse was clicked using mouseclk = pygame.mouse.get_pressed()
However this is incomplete and is not the standard way to implement event loops using the Pygame module. Here is how you do it (read the comment above each line to understand what it is doing) :
import pygame
from pygame.locals import *
...
# Main game loop - this is the while loop you have in your code. This is where frames are rendered on every new loop
while True:
#Get all event that have been triggered from the last frame
events = pygame.event.get()
# Now we go through each event separately
for event in events:
#Use an if statement to check what the event is. For example...
if event.type == MOUSEBUTTONDOWN: #If this event was a mouse button click
print("Clicked") #Do whatever function
elif event.type == QUIT: #If this event was quitting the game, i.e: pressing the X button on the game window
pygame.quit() #Close the game
#and so on. You can have as many elifs to check all the events you need
game.fill((0,0,0)) #... The rest of your code goes after the for loop (event loop)
So essentially in your main game loop (your while loop) you have inside it a for loop (the event loop), then after it back in the main while loop you have the rest of the code you need to run on every frame such as rendering images and animating sprites.
A Better Way: Rect Objects
You will find that to see if the mouse click position (x, y) was in range of the mouse button, you had to check if the mouse click's x value was in range of the X1 to X2 of the object you're clicking (top right and top left corners), and at the same time it also has to be in range Y1 to Y2 of the object (top right and bottom right corners), which effectively means the mouse was somewhere on the object when the mouse click event was triggered
For this, Pygame implements Rect objects. A rect can be explained as an imaginary rectangle with a width and height that you define when you create it ( I will show you how that is done in code below ). To be more realistic, it is a pygame object for storing rectangular co-ordinates (a range of all points within that rectangle).
Now the thing is that this rect object has methods like Rect.collidepoint, which will return True if the mouse click position is inside the rect. This is a more simplified, easier to understand way of detecting if a mouse click position is inside a rect or outside it.
So..
# Constructing a rect at position (0, 0) with width 100 and height 500
r = pygame.Rect(0, 0, 100, 50)
# Now lets assume that above there is code where a mouse click is detect and the position is stored in `mousepos`, where `mousepos[0]` is the X and `mousepos[1]` is the Y
# All we have to do now is tell pygame to check if this position is inside our Rect object *r* or not
if r.collidepoint(mousepos):
print("You clicked inside an imaginary box")
else:
print("You clicked outside my imaginary box")
# Simple!!!
Much easier and more readable! (especially after you remove the comments you will be dumbfounded by the simplicity of this (collidepoint means inside the rect).
In reality, this is not how you usually use rects, although it gives you the basic idea of what they are. Rects are used in a more object-oriented method where the button is a child class of the Pygame.sprite.Sprite class. Then you can assign a rect object as its self.image.rect property and use it from there. NOTE: this is not very detailed :D .
Also, this is not the only use of rects. You can check if one rect is inside another. You can check if a group of objects is inside a rect. You can even with only one line of code check whether one object of a group is inside a certain rect, and if so, remove it!
To understand more about how to code pygame from start to finish (as the extra part of this answer talking about events and rects is very incomplete and only there as a starting point), I would suggest this excellent yet very simple and easy to understand book. And all you need is a basic understanding of python syntax.

Actionscript 3- Random movement on stage? Also, Boundaries?

I'm trying to code something where there are creatures running back and forth, up and down across the stage, and I the player, have to try to go up to them, and pick them up. There are also boundaries on stage-
The map constraints- a big rectangle box is easy enough to accomplish. I've done this.
The boundaries within the map, which are also rectangles, but instead of bouncing the player back INSIDE the rectangle, I'm trying to do the opposite- keep the player out of it.
My code for it looks like this as of now:
//Conditions that check if player/monsters are hittesting the boxes (rocks
//and stuff), then if correct, bounce them away. Following code excludes
//the monsters for simplicity.
if((mcPlayer.x - aBounceBox[b].x) < 0 && mcPlayer.y <= (aBounceBox[b].y + aBounceBox[b].height/2) && mcPlayer.y >= (aBounceBox[b].y - aBounceBox[b].height/2))
{
mcPlayer.x = aBounceBox[b].x - aBounceBox[b].width/2 - mcPlayer.width/2;
}
//Duplicate above code for right side of box here
if((mcPlayer.y - (aBounceBox[b].y + aBounceBox[b].height/2)) < 0 && (mcPlayer.x + mcPlayer.width/2) > (aBounceBox[b].x - aBounceBox[b].width/2) && (mcPlayer.x - mcPlayer.width/2) < (aBounceBox[b].x + aBounceBox[b].width/2))
{
mcPlayer.y = aBounceBox[b].y + aBounceBox[b].height/2;
}
//Duplicate above code for Upper boundary of box here
The above doesn't work very well because the code to bounce for the left and right sides of the box conflicts with the upper and lower parts of the box I'm hit-testing for. Any ideas how to do that smoothly?
Also, another problem I am having is the pathing for the monsters in the game. I'm trying to get them to do the following:
Move around "organically", or a little randomly- move a little, stop. If they encounter a boundary, they'd stop and move, elsewhere. Not concerned where to, as long as they stop moving into rocks and trees, things like that.
Not overlap as much as possible as the move around on stage.
To push each other apart if they are overlapping, although I'd like to allow them to overlap very slightly.
I'm building that code slowly, but I thought I'd just ask if anyone has any ideas on how to do that.
To answer your first question, you may try to implement a new class/object which indicates the xy-offset between two display objects. In order to illustrate the idea more clearly, you can have a function similar to this:
public function getOffset(source:DisplayObject, target:DisplayObject):Object {
var dx:Number = target.x - source.x;
var dy:Number = target.y - source.y;
return { x:dx, y:dy };
}
Check if the hero character is colliding with another object first by hitTestObject(displayObj) of DisplayObject class. Proceed if the result is true.
Suppose you pass in your hero character as the source object, and another obstacle as the target object,
var offset:Object = getOffset(my_hero.mc, some_obstacle.mc);
After getting the resulting offset values, compare the magnitude (absolute value) of offset.x and offset.y. The outcome can be summarized as follows:
Let absDx be Math.abs(offset.x), absDy be Math.abs(offset.y),
absDx < absDy
offset.y < 0, target is above source
offset.y > 0, target is below source
absDx > absDy
offset.x < 0, target is to the left of source
offset.x > 0, target is to the right of source
absDx == absDy
refer to one of the above cases, doesn't really matter
Then you can update the position of your hero character according to different situations.
For your second question concerning implementing a very simple AI algorithm for your creatures, you can make use of the strategy above for your creatures to verify if they collide with any other stuff or not. If they do collide, assign them other directions of movement, or even simpler, just flip the signs(+/-) of their velocities and they will travel in opposite directions.
It is easier to implement simple algorithms first. Once it is working, you can apply whatever enhancements you like afterwards. For example, change directions when reaching junctions or per 3 seconds etc.

How do I HitTest two rotating objects properly? (A way to avoid bounding boxes)

I took an intro level flash course in college this semester and our final task was to make a mini-flash game.
I had to make a pipe-dream type game where there are a number of levels, and in each level you have to align the pipes so that the water flows and then you can pass to the next level.
I successfully made the first level, but upon making the second level,
where I placed a lot of curved pipes (by curved pipes I mean the attached image: ![Curved Pipe]: (http://imgur.com/mwpXAMn) )
I discovered that the method I use to decide when a level is complete is not working properly.
I was using HitTestObject, basically, I was testing whether 2 objects, Pipe_1, and Pipe_2, were intersecting. If all pipes intersected in the correct way, then procession to the next level is granted.
The problem with this I discovered is that flash has bounding boxes for movie clips you make, and that HitestObject uses bounding boxes to test for hits. Therefore, when you rotate a leftpipe so that it does not touch a straight pipe on screen, the bounding boxes still touch and it returns "collision" when in fact it is not actually touching on screen.
I looked up and found that you can use HitTestPoint but I can't figure out how to somehow make dynamic variables (that change upon rotation of object) that store one or two specific points on the leftpipe, say the two ends of it.
Once If I figure out how to get these values into a variable correctly, then I can figure out how to do HitTestpoint.
Also, I know of the LocaltoGlobal function but no matter what I try it keeps coming up with:
"Scene 1, Layer 'Layer 1', Frame 1, Line 30 1118: Implicit coercion of a value with static type Object to a possibly unrelated type flash.geom:Point."
meaning I don't know the correct code to store an x and a y coordinate as dynamic variables.
edit: ok since a person asked, I hunted this piece of code off the web and this is the one I was trying to play around with but to no avail:
How to use HitTest for 2 rectangles, r1 is rectangle 1 r2 is rectangle 2.
var r1width:Number = 135.0; //width of retangle 1 whith rotation 0
var r2width:Number = 93.0; //width of retangle 2 whith rotation 0
var p1:Object = {x:(r1width/2), y:(r1width/2)};
var p2:Object = {x:(-r1width/2), y:(r1width/2)};
var p3:Object = {x:(-r1width/2), y:(-r1width/2)};
var p4:Object = {x:(r1width/2), y:(-r1width/2)};
r1.localToGlobal(p1);
r1.localToGlobal(p2);
r1.localToGlobal(p3);
r1.localToGlobal(p4);
var p5:Object = {x:(r2width/2), y:(r2width/2)};
var p6:Object = {x:(-r2width/2), y:(r2width/2)};
var p7:Object = {x:(-r2width/2), y:(-r2width/2)};
var p8:Object = {x:(r2width/2), y:(-r2width/2)};
r2.localToGlobal(p5);
r2.localToGlobal(p6);
r2.localToGlobal(p7);
r2.localToGlobal(p8);
if((r2.hitTest(p1.x, p1.y, true))||(r2.hitTest(p2.x, p2.y, true))||(r2.hitTest(p3.x,
p3.y, true))||(r2.hitTest(p4.x, p4.y, true)))
{
trace('collision');
}
if((r1.hitTest(p5.x, p5.y, true))||(r1.hitTest(p6.x, p6.y, true))||(r1.hitTest(p7.x,
p7.y, true))||(r1.hitTest(p8.x, p8.y, true)))
{
trace('collision');
}
I did not write this code and it does not work. I'm not sure what "Object" is because I've never used it before, I'm assuming in this case it's sort of acting like a coordinate pair.
Also, this code is to hittest 2 rectangles, whereas I'm using an L-shaped pipe, so the x/y calculation would be quite different I imagine.
This code above gives the same error that I posted before:
Implicit coercion of a value with static type Object to a possibly unrelated type flash.geom:Point.
and it gives it first on line r1.localToGlobal(p1);
Instead of using Object, you need to use Point like so:
var p1:Point = new Point(r1width/2, r1width/2);

How to do slow motion effect in flash game with box2d

As i am working on top view racing game in which am trying to add slow motion effect when a car hits objects. I have tried with decreasing Stage.frameRate but the game appears lagging. and i have also tried with online tutorial called touch my pixel ( ref : http://blog.touchmypixel.com/2009/12/box2d-contactpoint-filtering/ ). But i didn't understand.
Is there any solution for showing such kind of slow motion effect. can anybody help me in this regard
Thanks and regards,
Chandrasekhar
Easiest way would be to have a global modifier property somewhere which can be used to multiply the movement of everything in the game.
For example, you could have the property speedModifier default to 1.
public var speedModifier:Number = 1;
And whenever you apply velocities, just multiply by the modifier:
body.SetLinearVelocity( new b2Vec2(x * speedModifier, y * speedModifier) );
This way all you need to do to half the speed of the game is to half the modifier:
speedModifier = 0.5;
To keep your code tidier and make managing this component of your game easier, there is probably a straightforward way to iterate over all of the bodies within the Box2D world and modify their velocities at the top of each update step. Something along the lines of:
for each(var i:b2Body in world.GetBodyList())
{
var currentVel:b2Vec2 = i.GetLinearVelocity();
var newVel:b2Vec2 = new b2Vec2(
currentVel.x * speedModifier,
currentVel.y * speedModifier
);
i.SetLinearVelocity( newVel );
}

Animation flickering problem

This is the game loop in my code and the drawing code:
float frames_per_second = 60;
display_timer = al_create_timer(1/frames_per_second);
queue = al_create_event_queue();
al_register_event_source(queue, al_get_timer_event_source(display_timer));
al_start_timer(display_timer);
while(!end_game)
{
ALLEGRO_EVENT event;
al_wait_for_event(queue, &event);
if(event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) break;
if(event.any.source == al_get_timer_event_source(display_timer))
{update_display();}
update_input();
}
void update_display()
{
al_clear_to_color(al_map_rgb(255, 255,255));
draw_objects(); //this is just an al_draw_bitmap() call
al_flip_display();
}
The animation created by moving objects on screen flickers, I'm surprised by this since I write to the back buffer of the screen thus I expect double buffering. What can I do to correct the flickering? Thanks.
Unrelated to the problem, you can check a timer by looking for the ALLEGRO_EVENT_TIMER event. You can use event.timer.source to check which timer it is, if you have more than one.
I think the main problem here is that you are drawing the graphics at 60fps but you are updating the input at an unlimited rate. This is actually backwards. You want to update the input at a fixed rate. You can draw the graphics as often as you like... although it makes no sense to update the graphics if nothing has changed.
So it should look something more like:
if(event.type == ALLEGRO_EVENT_TIMER)
{
update_input();
update_display();
}
However, this does not implement frame skipping if things get too slow. What you should do is set up the timer in a dedicated queue (that contains no other event sources). Then as long as there are events sitting in that dedicated timer queue, update the input.
Then update the display, assuming you've processed at least one tick. So you may, if things get too slow, do multiple input updates per drawn frame.