I'm working on a tic tac toe game in processing.
I can't figure out how to make a way to have X and O swap to vaguely imitate swapping turns I have seen someone did it like the code below but for some reason, I either get an error or it just doesn't work when I hook it up to my preexisting code.
I should have Elaborated more I plan (attempt) to use the Minimax algorithm to make this game unwinnable
print("Begin")
global top_left, top_middle, top_right
global middle_left, center, middle_right
global bottem_left, bottem_middle, bottem_right
#these are the variables used to check if someone has put their move their already
#0 = empty
#1 = Circle
#2 = X
top_left = 0
top_middle = 0
top_right = 0
middle_left = 0
center = 0
middle_right = 0
bottem_left = 0
bottem_middle = 0
bottem_right = 0
#code for changing turns
turn = 1
def turn_changer():
global turn
if turn == 1:
turn = 2
else:
turn = 1
#board setup
def setup():
size(600,600)
#this hurt my brain trying to fully understand
#lines dividing board
def draw():
for y in range(3):
for x in range(3):
rect(200*x,200*y,200,200)
#hope this is not what geomtry is like
#top left ellipse
if top_left == 1:
ellipse(100,100,150,150)
#top left X
elif top_left == 2:
line(0,0,200,200)
line(200,0,0,200)
#top middle ellipse
if top_middle == 1:
ellipse(300,100,150,150)
#top middle X
elif top_middle == 2:
line(200,0,400,200)
line(400,0,200,200)
#top right ellipse
if top_right == 1:
ellipse(500,100,150,150)
#top right X
elif top_right == 2:
line(400,0,600,200)
line(600,0,400,200)
#middle left ellipse
if middle_left == 1:
ellipse(100,300,150,150)
#middle left X
elif middle_left == 2:
line(0,200,200,400)
line(200,200,0,400)
#middle ellipse
if center == 1:
ellipse(300,300,150,150)
#middle X
elif center == 2:
line(200,200,400,400)
line(400,200,200,400)
#middle right ellipse
if middle_right == 1:
ellipse(500,300,150,150)
#middle right X
elif middle_right == 2:
line(400,200,600,400)
line(600,200,400,400)
#bottem left ellipse
if bottem_left == 1:
ellipse(100,500,150,150)
#bottem left X
elif bottem_left == 2:
line(0,400,200,600)
line(200,400,0,600)
#bottem middle ellipse
if bottem_middle == 1:
ellipse(300,500,150,150)
#bottem middle X
elif bottem_middle == 2:
line(200,400,400,600)
line(400,400,200,600)
#bottem right ellipse
if bottem_right == 1:
ellipse (500,500,150,150)
#bottem right Xw
elif bottem_right == 2:
line(400,400,600,600)
line(600,400,400,600)
#dectects the quardnates where the mouse clicked and prints them
def mousePressed():
println( (mouseX, mouseY) )
#top left square hitbox
if (mouseX > 0 and mouseX < 200) and (mouseY > 0 and mouseY < 200):
top_left =+ turn
turn_changer()
print("top left")
#top middle square hitbox
elif (mouseX > 200 and mouseX < 400) and (mouseY > 0 and mouseY < 200):
top_middle = turn
turn_changer()
print(turn)
print("top middle")
#top right square hitbox
elif (mouseX > 400 and mouseX < 600) and (mouseY > 0 and mouseY < 200):
top_right = turn
turn_changer()
print("top right")
#middle left square hitbox
elif (mouseX > 0 and mouseX < 200) and (mouseY > 200 and mouseY < 400):
middle_left = turn
turn_changer()
print("middle left")
#center square hitbox
elif (mouseX > 200 and mouseX < 400) and (mouseY > 200 and mouseY < 400):
center = turn
turn_changer()
print("middle")
#middle right square hitbox
elif (mouseX > 400 and mouseX < 600) and (mouseY > 200 and mouseY < 400):
middle_right = turn
turn_changer()
print("middle right")
#bottem left square hitbox
elif (mouseX > 0 and mouseX < 200) and (mouseY > 400 and mouseY < 600):
bottem_left = turn
turn_changer()
print("bottem left")
#bottem middle square hitbox
elif (mouseX > 200 and mouseX < 400) and (mouseY > 400 and mouseY < 600):
bottem_middle = turn
turn_changer()
print("bottem middle")
#bottem right square hitbox
elif (mouseX > 400 and mouseX < 600) and (mouseY > 400 and mouseY < 600):
bottem_right = turn
turn_changer()
print("bottem right")
I respect that you're learning on your own, and so I took some time to learn python's basics to give you something to think about. I'm not a python buff (yet), so I may have done some misguided manipulations somewhere (so if a better coder than me is reading this and spots something awful, let me know), but I believe that this is mostly good stuff.
I used class since I tend to think in OOP (and so will you after a while). Instead of seeing a grid with X and O, I see the game like this:
One game is an object.
A Game manages:
A Grid (which is an object, too).
Who's turn it is (and when it's the AI turn, how the AI should play).
When the game ends.
A grid manages:
9 Cases (which are objects, too).
A Case manages:
It draws itself (so... it's coordinates and stuff).
If there's a X or O on it.
If it's been clicked
I fully realize that objects are a huge bump in the learning curve when you start programming, but I'm insisting here because I've seen A LOT of hardcoding in your code, and it's the kind of stuff which will cause you problems when you scale up your projects.
Hardcoding, like how you check which case have been clicked, isn't inherently bad, but it makes everything more difficult. It's part of the things you sometimes learn "the hard way", so here's my advice: when you ninja-code something (short code snippets which are written quickly and won't be part of something bigger), it's not great but it does the job. In any other context, it must be motivated by some specific need to be a good practice, and even there it can be avoided most of the time.
Here's commented code based on what I just wrote. I didn't do the whole tic-tac-toe game, just up to the part where it switch turns between the players or the player/AI (I put a boolean up there that let you switch between human opponent and AI opponent). What is missing is mostly the AI logic (I put a temporary one where it selects the first case it finds) and the victory conditions.
The boolean is currently in "player vs player" mode. Change it to True to let the AI take over the O side.
# Player 1 (X) is human and play first
# Player 2 (O) is cpu
# You can change this boolean to play hotseat with a human if you want:
_AIPlayer = False
# Game own a grid, count turns and do any other game-specific concepts
# One "game of tic-tac-toe" would equal one of this object
class Game:
def __init__(self):
self.Grid = Grid(self) # creating the grid we'll use
self.TurnCount = 0 # first turn is turn number zero
def Render(self):
# when you draw the game, in fact it asks it's grid to draw itself
self.Grid.Render()
def Play(self):
# if it's the CPU's turn, let him play, else the game will wait for the player before going forward
# if there is no cpu player, the mouse can be used by player two
# the difference is that the cpu will do it's turn as a consequence of the player's turn
# and then add +1 to the turn count, while player 2 is exactly like player one but with O instead of X
# the game will check X and O to see who win, not a player class (but it could have been designed that way if needed)
if self.GetCurrentPlayer() == "O" and _AIPlayer:
self.AITurn()
def GetCurrentPlayer(self):
# return which's player is currently playing
if self.TurnCount % 2 == 0:
return "X"
else:
return "O"
def AITurn(self):
# this is a dumb placeholder
# your AI logic will be used here
# for now it just put a O on the first available case
print("AI turn")
for c in self.Grid.Cases:
if c.XO == "":
c.XO = self.GetCurrentPlayer()
break
self.TurnCount += 1
# Grid class is the whole grid
class Grid:
def __init__(self, game):
# the grid knows the game. I could use the global variable instead, but I dislike
# this kind of spaghetti. It would have worked, though.
# It's usually best to make everything you can dynamic, i.e. not hardcoded.
# It's easier to maintain and fix bugs that way, and you can upscale more easily too
# for an example, I could use this code to run several tic-tac-toe games in the
# same window at the same time with only a few modifications
self.Game = game
self.Cases = []
for i in range(3):
for j in range(3):
self.Cases.append(GridCase(i, j))
def Render(self):
# when you draw the grid, in fact it ask it's cases to draw themselves
for c in self.Cases:
c.Render()
def CaseClicked(self, xPos, yPos):
# this checks which case was clicked when it's a player
# since we don't care about the case's coordinated, we ask them if they have been clicked instead
for c in self.Cases:
if c.Clicked(xPos, yPos, self.Game.GetCurrentPlayer()):
self.Game.TurnCount += 1
return
# GridCase is each instance of 1 case in the grid
class GridCase:
def __init__(self, gridX, gridY):
# gridX and gridY are useful to know which case is part of which line
self.gridX = gridX
self.gridY = gridY
# I hardcoded the case's width and height, but you could totally make them dynamic
# and decide "on the fly" how big the grid will be. And it would still work.
self.w = 200 # width
self.h = 200 # height
# these coordinates are in pixels, and are useful to draw the case and for hit detection
self.x = self.w * gridX # x coordinate of the case
self.y = self.h * gridY # y coordinate of the case
# the "content" of the case
self.XO = "" # X or O as a character (it could be anything, I choose to stick to these)
def Render(self):
# the lines positions are dynamic: they'll be calculated from the case's perspective
# every case top left corner is in fact: (self.x, self.y)
rect(self.x, self.y, self.w, self.h)
# if the case has content, it'll be drawn at the same time than the case
if self.XO == "X":
line(self.x , self.y, self.x+self.w, self.y+self.h)
line(self.x, self.y+self.h, self.x+self.w, self.y)
elif self.XO == "O":
ellipse(self.x+(self.w/2),self.y+(self.h/2), self.w*0.75, self.h*0.75)
def SetXO(self, XO):
self.XO = XO
def Clicked(self, xPos, yPos, car):
# if the case is free and the click was inside it's boundaries, then attribute it to the current player
# the return True to tell that a sign was just placed
if self.XO == "" and xPos > self.x and xPos < self.x + self.w and yPos > self.y and yPos < self.y + self.h:
self.XO = car
return True
return False
# globals
_game = Game()
def setup():
size(600,600)
def draw():
# background wipes the screen "clean" (here it paints it black)
# then we can draw the current state of the grid
# here we could do without but I wanted you to know about it
background(0)
# draw the grid, then let the players do their thing
_game.Render()
# here you should check for game end conditions (victory or draw)
_game.Play()
def mouseClicked():
# listeing to mouse clicks
_game.Grid.CaseClicked(mouseX, mouseY)
You should copy and paste this code in a Processing.py IDE and try it. Fiddle around and read the comments. You can learn a lot here if you try. If you have questions, ask away in the comments with my handle and I'll come back and give you a hand.
And... have fun!
When the button is pressed check if the turn is equal to 1 or 2 and with that u tell the program to display an cross or a circle instead of making a function to change that value
Hope this helps!
There are SO MANY WAYS you could do this...
You can flip a variable (kinda like you just showed). Since there are only two players, I would probably flip a boolean instead: myBoolean = !myBoolean after every turn.
You can count the turns and use the modulo operator. Then you both know which turn it is right now AND the total number of turns for the current game. Let's say that you have a global TurnCount variable. If you do TurnCount % 2 the result will either be 0 or 1, making it a surefire way to know which turn it is. This operator is really useful and you should remember it either way!
If there are no difference in the gameplay between player 1 and 2 except for their X and O, you can flip between the character 'X' and the character 'O' instead of 1 and 2 like in your code snippet. This variable can be used to show visually which symbol will placed or straight up to place one symbol or the other. Simple and efficient.
So it mostly depends on how ninja you code will be. If you're doing a "simpel hot seat" where players takes turn clicking with the same mouse, method 3 is great. If you want to show "statistics" after the game, method 2 will work nicely. Method 1 will work too, but it's more rough, even if it's simple. You can add more code if you need tailored advice.
Have fun!
Set the variable 'turn' as a global (outside of the 'turn_changer' function / making sure this code is only ran once, not every time your game loops).
Run your game logic, with the player (X or O) being determined by the value of 'turn'.
After the player makes their move call 'turn_changer' so the turn changes.
Repeat.
Using the code you provided, I added this:
def game():
print (turn)
turn_changer()
print (turn)
turn_changer()
print (turn)
if __name__ == "__main__":
game()
and the turn_changer() function seems to work correctly
Related
I am making a one man pong game and I got everything except for moving the paddle up and down and I don't really understand how to do it can someone help. Is it something to do with the "pos" or is it something to do it with the syntax of the lines. The part where it controls the movements of the paddles is def move paddle
import tkinter
# steps
# ball diagonal
# paddle draw
# paddle animation with keyboard (right/left) -> challenge up/down
# collisions (if time)
# graphic parameters
canvas_width = 400
canvas_height = 500
ball_size = 30
timer_refresh = 20
paddle_width = 100
paddle_height = 20
# ball movement
y_move = 2
x_move = 2
# paddle movement
paddle_speed = 6
# game_state
game_running = True
def end_game():
global game_running
game_running = False
canvas.create_text(canvas_width/2, canvas_height/2, text="you lost!")
# move paddle when key is pressed
def move_paddle(event):
key_symbol = event.keysym
print(key_symbol)
pos = canvas.coords(paddle)
left = pos[0]
right = pos[2]
up = pos[1]
down = pos[3]
if key_symbol == "Left" and left > 0:
canvas.move(paddle, -paddle_speed, 0)
elif key_symbol == "Right" and right <= canvas_width:
canvas.move(paddle, paddle_speed, 0)
# move paddle up
elif key_symbol == "Up" and up >= 0:
canvas.move(paddle, paddle_speed, 0)
# move paddle down
elif key_symbol == "Down" and down <= canvas_width:
canvas.move(paddle, paddle_speed, 0)*
def collision(ball_pos):
overlap_result = canvas.find_overlapping(ball_pos[0],ball_pos[1],ball_pos[2],ball_pos[3])
if paddle in overlap_result:
return True;
else:
return False;
# draw/move ball
def draw():
global y_move, x_move
canvas.move(ball1, x_move, y_move)
pos = canvas.coords(ball1)
top_y = pos[1]
bottom_y = pos[3]
left = pos[0]
right = pos[2]
if top_y <= 0:
y_move = -y_move
elif bottom_y >= canvas_height-5:
y_move = -y_move
end_game()
# did I hit left or right wall?
elif left <= 0 or right >= canvas_width-5:
x_move = -x_move
# did I collide with the paddle? if so bounce vertically
if collision(pos):
y_move = -y_move
# animation timer
def master_timer():
# draw/move ball
draw()
# tkinter processing
tk.update_idletasks()
tk.update()
if game_running:
tk.after(timer_refresh, master_timer)
tk = tkinter.Tk()
tk.title("Simplified Pong")
# block resizing window
tk.resizable(0,0)
# drawing the canvasd
canvas = tkinter.Canvas(tk, width=canvas_width, height=canvas_height, bd=0, highlightthickness=0)
canvas.pack()
ball1 = canvas.create_oval(0, 0, ball_size, ball_size, fill="red")
canvas.move(ball1, canvas_width/2, canvas_height/2)
paddle = canvas.create_rectangle(0,0,paddle_width, paddle_height, fill="black")
canvas.move(paddle, canvas_width/2, canvas_height/1.2)
canvas.bind_all("<KeyPress-Right>", move_paddle)
canvas.bind_all("<KeyPress-Left>", move_paddle)
canvas.bind_all("<KeyPress-Up>", move_paddle)
canvas.bind_all("<KeyPress-Down>", move_paddle)
master_timer()
tk.mainloop()
The problem is quite simple if you refer to the docs of move method, for tkinter, or have an understanding of how to use it. From the docs:
.move(tagOrId, xAmount, yAmount)
Moves the items specified by tagOrId by adding xAmount to their x coordinates and yAmount to their y coordinates.
So if you notice, it takes the x coords first and then the y coords. So when you want to translate(move) upon the y axis(up and down), you want to alter the y axis argument, but instead you are doing it for the x axis. So you have to pass the variable onto the correct parameter only.
elif key_symbol == "Up" and up >= 0:
canvas.move(paddle, 0, -paddle_speed)
# move paddle down
elif key_symbol == "Down" and down <= canvas_width:
canvas.move(paddle, 0, paddle_speed)
Also note that you can get rid of all the key binds and just keep a single general bind, because inside your function, you are already getting the key that is pressed dynamically, so it doesn't make much sense to bind to all possible keys:
canvas.bind_all("<KeyPress>", move_paddle) # Instead of Up, Down, Left and Right
Also another tip is, you can take advantage of * to pass the contents of the iterable as the argument to the function:
canvas.find_overlapping(*ball_pos) # Instead of ball_pos[0] and so on
I am new to programming and started with pygame zero. I am making a little game where you shoot a rocket to an alien. But my rocket keeps stuck to the border when fired, I made a reload function but I want it to go automatically ( when it hits the border or alien to go back to its normal position). Can anyone help me with that?
alien = Actor('alien', (100,100))
ship =Actor('ship', (500,400))
rocket_fire = Actor('rocket_fire', (500,400))
WIDTH = 1000
HEIGHT =500
def draw():
screen.clear()
screen.blit('space_back', (0,0))
rocket_fire.draw()
ship.draw()
alien.draw()
def move_ship(ship):
if keyboard.left:
ship.x -= 3
rocket_fire.x -= 3
elif keyboard.right:
ship.x += 3
rocket_fire.x += 3
elif keyboard.space:
animate(rocket_fire, pos = (ship.x,0))
elif keyboard.r:
rocket_fire.pos = (ship.x,ship.y)
def move_alien(alien):
alien.right +=2
if alien.left > WIDTH:
alien.right = 0
collide = rocket_fire.colliderect(alien)
if collide == 0:
alien.image = 'alien'
elif collide == 1:
alien.image = 'nuclear_explosion'
def update():
rocket_fire.draw()
ship.draw()
alien.draw()
move_ship(ship)
move_alien(alien)
You can try to assign the initial values to 'rocket_fire' after 'collide == 1'.
elif collide == 1:
alien.image = 'nuclear_explosion'
// rocket_fire = (500,400) -> this is just a representation to assign position; not accurate as per the code
We were making tic tac toe and we used pygame. We were casually just changing coordinates but for some reason, after coding for the 5th square it stopped working? Does anyone know what's up? Please see below for the code. We were adding lines of code for identifying where the text 'X' or 'O' should go based on what region the moue was clicked in. But, for some reason (what is it?) the last elif statement never works, and therefore the x or o never appears. Thanks for taking the time to help.
import pygame
pygame.init()
width = 600
height = 600
Board = pygame.display.set_mode((width, height))
pygame.display.update()
running = True
pygame.draw.lines(Board, (255, 255, 153), False, [(0,200), (300,200), (600,200)], 1)
pygame.draw.lines(Board, (255, 255, 153), False, [(0,400), (300,400), (600,400)], 1)
pygame.draw.lines(Board,(255,255,153), False, [(200, 0), (200,300), (200,600)],1)
pygame.draw.lines(Board,(255,255,153), False, [(400, 0), (400,300), (400,600)],1)
pygame.display.update()
def text_objects(text, font):
textSurface = font.render(text, True, (255,255,153))
return textSurface, textSurface.get_rect()
def message_display(text, position):
largeText = pygame.font.Font('freesansbold.ttf',115)
TextSurf, TextRect = text_objects(text, largeText)
TextRect.center = position
Board.blit(TextSurf, TextRect)
pygame.display.update()
def toX(tuple):
x = tuple[0]
return x
def toY(tuple):
y = tuple[1]
return y
def makeX(pos):
if toX(pos)<=200 and toY(pos)<=200:
position = (100, 100)
message_display(letter, position)
elif toX(pos)<=400 and toX(pos)>200:
if toY(pos) <= 200:
position = (300,100)
message_display(letter, position)
elif toX(pos)<=600 and toX(pos)>400:
if toY(pos) <= 200:
position = (500,100)
message_display(letter, position)
elif toX(pos)<=200:
if toY(pos) <= 400 and toY(pos) > 200:
position = (100,300)
message_display(letter, position)
x = 0
while (running):
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
if x == 0:
letter = "X"
x = 1
else:
letter = "O"
x = 0
makeX(pos)
Akisaat, as you likely realize, this forum isn't set up for your sort of programing-help request. Stack Overflow is more for technical questions by experienced programmers. There are web sites out there geared to your learning level. This site is not.
That having been said, just this once, let's have a look at your code and see where you went wrong.
For most of your squares, the top elif test is catching the X, but the inner Y test only works for a single square, while all other values of Y are being ignored. As a result, once the elif test catches an X value and fails to find a Y value that it can use, control drops out of the if-elif chain and those squares are never handled.
For example, this line catches ALL clicks in the middle column...
elif toX(pos)<=400 and toX(pos)>200:
..but the nested 'if' test catches ONLY the TOP row. ALL OTHER squares are lost
when control drops out of the if-elif chain. You need to test FIRST for a row, then test for all three columns contained in that row. You are only testing for ONE square in any given row.
But I would suggest a different approach, that you put ALL of the row and column tests on a single line. That way you can have separate blocks of code, one for each unique square, without too much nesting going on within any single block of code.
For example:
elif toX(pos)<=400 and toX(pos)>200 and toY(pos) < 200: # If Column 1 Row 0 Clicked..
Then do something in that square.
Following the logic of your program will be easier for you if you use comments to name your squares and use print() commands to give reports so you can verify that your code is behaving as planned. You can always go back and remove those lines once your code is proven. Try something like this in your code..
# As an aid, let's identify the squares for our logic block below
# | |
# Square [0,0] | Square [1,0] | Square [2,0]
# | |
# --------------------------------------
# | |
# Square [0,1] | Square [1,1] | Square [2,1]
# | |
# --------------------------------------
# | |
# Square [0,2] | Square [1,2] | Square [2,2]
# | |
def makeX(pos):
# Square [0,0]
if toX(pos)<=200 and toY(pos)<=200: # If the far left column and the top row is Clicked
print(" Square [0,0] Clicked")
position = (100, 100)
message_display(letter, position)
# Square [1,0]
elif toX(pos)<=400 and toX(pos)>200: # If the middle column is clicked
print(" Column 1 Clicked")
if toY(pos) <= 200: # And then if the top Row is clicked
print(" Row 0 Clicked")
position = (300,100)
message_display(letter, position)
# Square [2,0]
elif toX(pos)<=600 and toX(pos)>400 and toY9pos) <= 200: # If the far right Column
print(" Column 2 Clicked")
if toY(pos) <= 200: # And then if the top row is clicked
print(" Row 0 Clicked")
position = (500,100)
message_display(letter, position)
# Square [0,1]
elif toX(pos)<=200: # If the far left column is clicked
print(" Column 0 Clicked")
if toY(pos) <= 400 and toY(pos) > 200: # And then if the middle Row is Clicked
print(" Row 1 Clicked")
position = (100,300)
message_display(letter, position)
Doing this to check the logic of your code, and correcting the error I identified earlier should get you well on your way to writing your tick-tac-toe project. Good luck and have fun!
I've updated your makeX function, separating out the determination of the x and y coordinates:
def makeX(pos):
if toX(pos)<=200:
pos_x = 100
elif toX(pos) <= 400:
pos_x = 300
else:
pos_x = 500
if toY(pos) <= 200:
pos_y = 100
elif toY(pos) <= 400:
pos_y = 300
else:
pos_y = 500
message_display(letter, (pos_x, pos_y))
I've minimised the changes so it's easier to understand.
With your existing logic, you were only detecting clicks within the first four cells. You could have explicitly extended the logic for the remaining cells, but that is too repetitive and brittle.
Once you're happy with this solution, you need to consider separating the board state from your drawing functions, so you can check for victory and prevent redrawing in the same position.
I've just started learning Python (as of now the only other programming language I'm fluent in is C, so this is all very new to me), and I've decided to make some simple game with it just to get used to it. I'd like it to be an arrow keys controlled game and I'm using the graphics.py library which to my understanding is very beginner friendly.
This is what I came up with:
from graphics import *
win = GraphWin("Game", 800, 500)
def getXInput():
inputBuffer = win.checkKey()
inputResult = 0
if inputBuffer == "Right":
inputResult = 1
elif inputBuffer == "Left":
inputResult = -1
return inputResult
def getYInput():
inputBuffer = win.checkKey()
inputResult = 0
if inputBuffer == "Down":
inputResult = 1
elif inputBuffer == "Up":
inputResult = -1
return inputResult
class player:
def __init__(self):
self.x = 400
self.y = 250
self.speed = 3
self.canMove = True
self.txt = Text(Point(self.x, self.y), "test")
self.txt.draw(win)
def move(self):
while self.canMove == True:
xMove = getXInput() * self.speed
yMove = getYInput() * self.speed
self.txt.move(xMove, yMove)
def main():
pl = player()
pl.move()
win.close()
main()
Player input is managed through the getXInput and getYInput functions, which costantly checks if the arrow keys are pressed and return -1, 0 or 1 accordingly.
Then the result of those functions is costantly multiplied for the player's attribute "speed" and that's how the game is able to move the player character in the correct direction. This kinda works: it works perfectly on the x axis, but it acts weird on y axis, registering inputs only once in a while. Putting the yMove assignment before the xMove one within the move method reverses the problem, making vertical movement perfect and horizontal movement very sloppy. I really can't understand what's causing this, can anybody help?
I have an assignment that asks me to do the following:
Use Google's advanced image search to find a reasonably-sized image of a ball that is free to reuse and that includes transparency. Modify the sample code so that your ball slides back and forth across the bottom of the screen. It should take 2 seconds for the ball to go from the left side to the right.
Improve your animation for question 5 so that the ball rotates, accurately, as if it were rolling back and forth.
Modify your animation for question 6 so that the ball travels counterclockwise around the edge of the screen
I am at the last part. Trying to modify the animation for question 6 to do this: (1:24)
http://www.youtube.com/watch?v=CEiLc_UFNLI&feature=c4-overview&list=UUpbgjjXBL3hdTKDZ0gZvdWg
I'm stumped pretty bad. I just can't seem to understand how I will get the ball to slowly move from one point to another. The ball is an image. This is what I have so far, but it doesn't work.
"""Some simple skeleton code for a pygame game/animation
This skeleton sets up a basic 800x600 window, an event loop, and a
redraw timer to redraw at 30 frames per second.
"""
from __future__ import division
import math
import sys
import pygame
class MyGame(object):
def __init__(self):
"""Initialize a new game"""
pygame.mixer.init()
pygame.mixer.pre_init(44100, -16, 2, 2048)
pygame.init()
# set up a 640 x 480 window
self.width = 800
self.height = 600
self.img = pygame.image.load('ball.png')
self.screen = pygame.display.set_mode((self.width, self.height))
self.x = 0
self.y = 0
self.angle = 0
self.rotate_right=True
self.first = True
#0: Move bottomleft to bottomright 1: Move from bottomright to topright 2:Move from topright to topleft 3:Move from topleft to bottomleft
self.mode = 0
# use a black background
self.bg_color = 0, 0, 0
# Setup a timer to refresh the display FPS times per second
self.FPS = 30
self.REFRESH = pygame.USEREVENT+1
pygame.time.set_timer(self.REFRESH, 1000//self.FPS)
def get_mode(self):
rect = self.img.get_rect()
if self.first == True:
self.first = False
return
if (self.x, self.y) == (0, self.height - rect.height):
#Our starting point, bottom left
self.mode = 0
elif (self.x, self.y) == (self.width-rect.width, self.height-rect.height):
#Bottom right
self.mode = 1
elif (self.x, self.y) == (self.width-rect.width, 0):
#Top Right
self.mode = 2
elif (self.x, self.y) == (0,0):
#Top Left
self.mode = 3
def get_target(self):
rect = self.img.get_rect()
if self.mode == 0:
targetPosition = (0, self.height - rect.height)
elif self.mode == 1:
targetPosition = (self.width-rect.width, self.height-rect.height)
elif self.mode == 2:
targetPosition = (self.width-rect.width, 0)
elif self.mode == 3:
targetPosition = (0,0)
return targetPosition
def get_angle(self):
if self.angle == 360:
self.rotate_right = False
elif self.angle == 0:
self.rotate_right = True
if self.rotate_right == True:
self.angle+=12
else:
self.angle-=12
def run(self):
"""Loop forever processing events"""
running = True
while running:
event = pygame.event.wait()
# player is asking to quit
if event.type == pygame.QUIT:
running = False
# time to draw a new frame
elif event.type == self.REFRESH:
self.draw()
else:
pass # an event type we don't handle
def draw(self):
"""Update the display"""
# everything we draw now is to a buffer that is not displayed
self.screen.fill(self.bg_color)
#Draw img
rect = self.img.get_rect()
#Note: this can be made dynamic, but right now since this is typically a poor structure, we will use static values.
#80 is the padding, so it hits right before.
#0,0 : top left
#self.width-rect.width, 0 : top right
#0, self.height-rect.height : bottom left
#self.width-rect.width, self.height-rect.height : bottom right
targetPosition = ()
#img = pygame.transform.rotate(self.img, self.angle)
img = self.img
self.get_angle()
self.get_mode()
targetPosition = self.get_target()
print targetPosition
print self.x, self.y
if self.x < targetPosition[0]:
self.x+= targetPosition[0]-self.x//self.FPS
elif self.x > targetPosition[0]:
self.x-= targetPosition[0]+self.x//self.FPS
if self.y < targetPosition[1]:
print "s"
self.y+= targetPosition[1]-self.y//self.FPS
elif self.y > targetPosition[1]:
self.y-= targetPosition[1]+self.y//self.FPS
rect = rect.move(self.x, self.y)
self.screen.blit(img, rect)
# flip buffers so that everything we have drawn gets displayed
pygame.display.flip()
MyGame().run()
pygame.quit()
sys.exit()
What's happening is that your ball is starting at (0,0) (top left) with a target of (0,550) (bottom left), discovers that it's at a lower y than its target, and promptly proceeds to increment its position by
targetPosition[1] - (self.y // self.FPS)
which is of course equal to 550, so it immediately snaps to the bottom of the screen.
Then during the next draw loop, get_mode() comes along and says 'okay, I'm at (0, 550), so I'll go ahead and set the mode to 0'. Then get_target() comes along and says 'okay, I'm in mode 0, let's go over to (0, 550).
And then this happens again during the next draw loop, and the next, and the next ... So of course your ball doesn't go anywhere.
You'll need to do a couple of things to fix your example:
Fix your target positions in get_target(). Right now they're targeting the same points where the transitions that trigger those modes happen, so your ball won't go anywhere.
Consider your velocity statements more carefully: right now they'll behave somewhat strangely. One way to do this properly is to determine (dx, dy) - that is, the absolute vector from you to your destination - and then normalize this vector such that it points in the same direction but has a magnitude equal to your desired speed. This approach will work for any target position you want.
To elaborate on the second point:
Suppose we're at (x, y) and we're trying to get to (target_x, target_y).
Let dx = target_x - x, dy = target_y - y. This should be uncontroversial: we're just taking the difference.
Then we remember the Pythagorean theorem: given a right triangle with sides a, b, c and hypotenuse c, we recall that len(c)**2 == len(a)**2 + len(b)**2. It's the same thing with vectors: the length of a vector (x, y) is the hypotenuse of a right triangle with side lengths x and y. You can draw this on a piece of paper if you want to prove this to yourself.
Given that, we can find the length of (dx, dy): it's just L(dx, dy) = sqrt(dx*dx + dy*dy). This lends itself to a curious observation: if we multiply both dx and dy by a scalar k, we also multiply the length by k, since sqrt(dx*k*dx*k + dy*k*dy*k) == sqrt(k*k*(dx*dx + dy*dy)) == k*sqrt(dx*dx + dy*dy).
It follows that we can find a vector parallel to (dx, dy), but of length 1, by dividing both dx and dy by L(dx, dy). Precompute L to avoid some potential issues. Multiply this new vector by whatever you want your speed to be: this is your desired velocity.