pygame graphics grid - graphics

Hello, I am new to Python and graphics programming in general. At present I am messing around with creating grids as practice, and I am having problems with getting pygame to put objects on top of the surface window.
Below is my code with comments. I reckon that the problem may be to do with the blit function but I am unsure. Any help would be appreciated. Also, the Python shell does not highlight any exceptions or errors, but the program does not run.
import sys, random, pygame
from pygame.locals import *
def main():
#settings
boardDims = (20,20) #number of cells on board
#pygame settings
cellDims = (20,20) #number of pixels on cells
framerate = 50
colours = {0:(0,0,0), 1:(255,255,255)}
#pygame
pygame.init()
#sets x and y pixels for the window
dims = (boardDims[0] * cellDims[0],
boardDims[1] * cellDims[1])
#sets window, background, and framrate
screen = pygame.display.set_mode(dims)
background = screen.convert()
clock = pygame.time.Clock()
#create new board
board = {}
#iterates over x and y axis of the window
for x in range(boardDims[0]):
for y in range(boardDims[1]):
board[(x,y)] = 0 #sets whole board to value 0
board[(1,1)] = 1 #sets one square at co-ordinates x=1,y=1 to cell
# value 1
return board
running = 1
while running:
# 1 PYGAME
#get input
for event in pygame.event.get():
if event.type == QUIT or \
(event.type == KEYDOWN and event.key == K_ESCAPE):
running = 0
return
#link pygames clock to set framerate
clock.tick(framerate)
for cell in board:
#adds cells dimensions and co-ordinates to object rectangle
rectangle = (cell[0]*cellDims[0], cell[1]* cellDims[1],
cellDims[0], cellDims[1])
#pygame draws the rectabgle on the background using the relevant
#colour and dimensions and co-ordinates outlined in 'rectangle'
square = pygame.draw.rect(background, colours[board[cell]],
rectangle)
#blits and displays object on the background
background.blit(square)
screen.blit(background, (0,0))
pygame.display.flip()
if __name__ == "__main__":
main()

The "return board" statement is exiting your program before any blitting is done.
Also, you can blit the squares directly on the screen variable, ie: you don't need the background variable.

Related

Cloning issues when trying to move a sprite pygame [duplicate]

I'm building a pong game trying to get better at programming but Im having trouble moving the ball. When the move_right method is called the ellipse stretches to the right instead of moving to the right. I've tried putting the ball variable in the init method but that just makes it not move at all even though the variables should be changing on account of the move_right method. I have also tried setting the x and y positions as parameters in the Ball class,but that just stretches it also.
I don't understand why when I run the following code the ball I'm trying to move stretches to the right instead of moves to the right. Can someone explain why this is happening? I have tried everything I can think of but i can't get it to do what I want.
import pygame,sys
import random
class Ball:
def __init__(self):
self.size = 30
self.color = light_grey
self.x_pos = width/2 -15
self.y_pos = height/2 -15
self.speed = 1
#self.ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
def draw_ball(self):
ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
pygame.draw.ellipse(screen,self.color,ball)
def move_right(self):
self.x_pos += self.speed
class Player:
def __init__(self,x_pos,y_pos,width,height):
self.x_pos = x_pos
self.y_pos = y_pos
self.width = width
self.height = height
self.color = light_grey
def draw_player(self):
player = pygame.Rect(self.x_pos,self.y_pos,self.width,self.height)
pygame.draw.rect(screen,self.color,player)
class Main:
def __init__(self):
self.ball=Ball()
self.player=Player(width-20,height/2 -70,10,140)
self.opponent= Player(10,height/2-70,10,140)
def draw_elements(self):
self.ball.draw_ball()
self.player.draw_player()
self.opponent.draw_player()
def move_ball(self):
self.ball.move_right()
pygame.init()
size = 30
clock = pygame.time.Clock()
pygame.display.set_caption("Pong")
width = 1000
height = 600
screen = pygame.display.set_mode((width,height))
bg_color = pygame.Color('grey12')
light_grey = (200,200,200)
main = Main()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#player = pygame.Rect(width-20,height/2 -70,10,140)
#opponent = pygame.Rect(10,height/2-70,10,140)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#pygame.draw.rect(screen,light_grey,player)
#pygame.draw.rect(screen,light_grey,opponent)
#pygame.draw.ellipse(screen,light_grey,ball)
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
clock.tick(60)
You have to clear the display in every frame with pygame.Surface.fill:
while True:
# [...]
screen.fill(0) # <---
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
# [...]
Everything that is drawn is drawn on the target surface. The entire scene is redraw in each frame. Therefore the display needs to be cleared at the begin of every frame in the application loop. The typical PyGame application loop has to:
handle the events by either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by either pygame.display.update() or pygame.display.flip()

Pygame - put updating mouse coords on the screen

Can anyone help me figure out how to put constantly updating ouse coords in the upper right corner of the screen? I think it would be a big help to have it there just for dev purposes, but I cant figure it out. Here is my code so far:
'''
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Oct 17 20:26:57 2020
#author: wohlsr
"""
import pygame
from pygame.locals import *
from sys import exit
background_image_filename = ('menu-start.jpg')
mouse_image_filename = ('mouse-pointer.png')
icon = pygame.image.load('go-stop-icon.png')
pygame.init()
screen = pygame.display.set_mode((1024, 768), 0, 32)
pygame.display.set_caption("Gostop")
pygame.display.set_icon(icon)
background = pygame.image.load(background_image_filename).convert()
mouse_cursor = pygame.image.load(mouse_image_filename).convert_alpha()
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
pygame.mouse.set_visible(0)
screen.blit(background, (0,0))
x, y = pygame.mouse.get_pos()
x-= mouse_cursor.get_width() / 2
y-= mouse_cursor.get_height() / 2
screen.blit(mouse_cursor, (x, y))
pygame.display.update()
'''
Visual of what I have so far
This code puts the mouse position in the top right corner of the screen.
import pygame
from pygame.locals import *
from sys import exit
background_image_filename = ('menu-start.jpg')
mouse_image_filename = ('mouse-pointer.png')
icon = pygame.image.load('go-stop-icon.png')
pygame.init()
screen = pygame.display.set_mode((1024, 768), 0, 32)
pygame.display.set_caption("Gostop")
pygame.display.set_icon(icon)
background = pygame.image.load(background_image_filename).convert()
mouse_cursor = pygame.image.load(mouse_image_filename).convert_alpha()
font=pygame.font.SysFont('arial',20,True,False) # font for mouse position
def showcoords(screen,x,y):
q1Text= font.render("{} {}".format(int(x),int(y)).center(20),1,(0,0,0)) # surface with coord text
bg = q1Text.copy() # copy surface for background
bg.fill((0,100,100)) # background
textRect = q1Text.get_rect()
textRect.topright = ((1024,0)) # put in top\right corner
screen.blit(bg,textRect) # draw background
screen.blit(q1Text,textRect) # draw coords
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
pygame.mouse.set_visible(0)
screen.blit(background, (0,0))
x, y = pygame.mouse.get_pos()
x-= mouse_cursor.get_width() / 2
y-= mouse_cursor.get_height() / 2
screen.blit(mouse_cursor, (x, y))
showcoords(screen,x,y) # show mouse coords
pygame.display.update()
If you want to use the global x,y values without passing parameters, make these changes:
def showcoords(screen):
global x,y # use existing x,y values
.......
showcoords(screen) # show mouse coords
Output

window re-sizing using tkinter

I've coded a GUI based Othello game. Am having a bit trouble re-sizing the game board. here is the code I have so far:
BOXWIDTH=60
BOXHEIGHT=60
class player:
"""Make a user player to play the game via a GUI."""
def __init__(self,row,column):
# create the GUI state variables
self.alive = True
self.move = None
self.move_played = False
# create the GUI windows and handlers
self.root = tkinter.Tk()
self.root.wm_title('Othello')
self.root.protocol("WM_DELETE_WINDOW", self.quit)
# create a button to get the No move command from the user
tkinter.Button(self.root, text="No Move", command = self.nomove).pack()
# create a label for displaying the next player's name
self.movemesg = tkinter.StringVar()
tkinter.Label(self.root, textvariable=self.movemesg).pack()
self.canvas = tkinter.Canvas(self.root, bg="darkgreen",
height = BOXHEIGHT*row,
width = BOXWIDTH*column)
self.canvas.bind("<Button-1>", self.click)
# create a box for highlighting the last move
self.lastbox = self.canvas.create_rectangle(0, 0, BOXWIDTH*column,
BOXHEIGHT*row,
outline="red")
# draw the game canvas
for i in range(1,row):
# horizontal lines
self.canvas.create_line(0, i*BOXHEIGHT,
BOXWIDTH*column, i*BOXHEIGHT)
for i in range(1,column):
# vertical lines
self.canvas.create_line(i*BOXWIDTH, 0,
i*BOXWIDTH, BOXHEIGHT*row)
# the board will store the widgets to be displayed in each square
self.board = [[None for y in range(row)]
for x in range(column)]
# display the window
self.canvas.pack()
self.canvas.focus_set()
self.root.update()
def draw_board(self, game, last_move):
"""Draw an othello game on the board."""
ws = str(sum(x.count(1) for x in game.board))
bs = str(sum(x.count(-1) for x in game.board))
if game.player == -1:
self.movemesg.set("Black to play "+'\nSCORE: White = '+ws+' Black = ' + bs)
else:
self.movemesg.set("White to play "+'\nSCORE: White = '+ws+' Black = ' + bs)
for i in range(game.column):
for j in range(game.row):
color = game.get_color((i,j))
if color == -1:
board_color = "black"
elif color == 1:
board_color = "white"
else:
if self.board[i][j] is not None:
self.canvas.delete(self.board[i][j])
self.board[i][j] = None
continue
if self.board[i][j] is None:
self.board[i][j] = self.canvas.create_oval(
i*BOXWIDTH+2, j*BOXHEIGHT+2, (i+1)*BOXWIDTH-2,
(j+1)*BOXHEIGHT-2, fill = board_color)
else:
self.canvas.itemconfig(self.board[i][j], fill=board_color)
I want the game board to resize i.e it must cause the area in which the board is drawn to change size correspondingly, with the game board redrawn to fill the available space. Any help on this would be great.
Your first problem is that the canvas wasn't told to grow when the main window grows. Change your pack statement to this:
self.canvas.pack(fill="both", expand=True)
Next, you will need to create a binding that gets called whenever the canvas resizes. You can do this by binding to <Configure>. For example:
self.canvas.bind("<Configure>", self.on_configure)
Finally, you will need to create a function that does nothing except redraw the board. You will call this method from the on_configure method.

Pygame repaint_rect with layereddirty [duplicate]

I am trying do end credits like animation the code above for Title crawl, I am trying to make the following changes to it:- 1) The text should begin at the bottom of screen at certain location, such that no other text from the string should be displayed below that location on the screen. 2) The text should stop at certain location on top of screen such that the line at the top should be deleted as soon as it reaches that location making room for other lines in the string.
I am a python newbie, I am just experimenting with things, the following code doesn't belong to me either.
import pygame
from pygame.locals import *
pygame.init()
pygame.display.set_caption('Title Crawl')
screen = pygame.display.set_mode((1000, 800))
screen_r = screen.get_rect()
font = pygame.font.SysFont("franklingothicdemibold", 40)
clock = pygame.time.Clock()
def main():
crawl = ["Star Wars - The Wilds"," ","It is a dark time for the Galaxy. The evil Dark","Lord, Vitiate is rising to power. Alone, a single", "spec is on a trip, a trip that will ultimately", "rectify the wrongs of the galaxy. The keepers ", "of peace are dying out and the DARK SIDE is", "lurking, a conniving force determined to", "become the omniarch."]
texts = []
# we render the text once, since it's easier to work with surfaces
# also, font rendering is a performance killer
for i, line in enumerate(crawl):
s = font.render(line, 1, (229, 177, 58))
# we also create a Rect for each Surface.
# whenever you use rects with surfaces, it may be a good idea to use sprites instead
# we give each rect the correct starting position
r = s.get_rect(centerx=screen_r.centerx, y=screen_r.bottom + i * 45)
texts.append((r, s))
while True:
for e in pygame.event.get():
if e.type == QUIT or e.type == KEYDOWN and e.key == pygame.K_ESCAPE:
return
screen.fill((0, 0, 0))
for r, s in texts:
# now we just move each rect by one pixel each frame
r.move_ip(0, -1)
# and drawing is as simple as this
screen.blit(s, r)
# if all rects have left the screen, we exit
if not screen_r.collidelistall([r for (r, _) in texts]):
return
# only call this once so the screen does not flicker
pygame.display.flip()
# cap framerate at 60 FPS
clock.tick(60)
if __name__ == '__main__':
main()
Use set_clip() to set the clipping region of the display surface.
e.g. Clip 100 rows at the top and the bottom:
# set clipping rectangle
clip_rect = (0, 100, screen.get_width(), screen.get_height()-200)
screen.set_clip(clip_rect)
for r, s in texts:
# now we just move each rect by one pixel each frame
r.move_ip(0, -1)
# and drawing is as simple as this
screen.blit(s, r)
# cancel clipping for further drawing
screen.set_clip(None)

Pygame sprite clamping isse

So Ive been having issues with getting a sprite to stay withing the bounds of the screen. I got it to work with a simple rect(0,0,16,16), but i cant seem to get it to work with a sprite being blit onto the screen. What do i need to change in order to keep my sprite clamped within the screen res? I only just started today using classes to orgonize code so any input is appreciated and helpful.
import pygame
from pygame.locals import *
from pygame import Color
class Game():
""" Lets try to get this going by simple steps
One by one. First step, lets figure how to make a class
that can do the display stuff. Lord have mercy on my soul"""
def __init__(self, wi=256, hi=224, multii=3):
"""Initialization"""
pygame.init()
self.runGame = True
self.width = wi*multii
self.height = hi*multii
self.spritesize = 16*multii
self.clock = pygame.time.Clock()
self.fps = self.clock.get_fps()
self.screen = pygame.display.set_mode((self.width, self.height))
self.kl = []
self.walk = [0, 0]
self.speed = multii*1.5
self.x,self.y = self.width/2, self.height/2
self.playerSpr = pygame.image.load('images/'+'link1.png').convert_alpha()
self.playerRec = Rect(self.playerSpr.get_rect())
def mainLoop(self):
"""Loop through the main game routines
1. Drawing 2. Input handling 3. Updating
Then loop through it until user quits"""
while self.runGame:
self.clock.tick(60)
self.events()
self.draw()
def events(self):
"""Time to handle some events"""
for e in pygame.event.get():
if (e.type == pygame.QUIT) or (e.type == KEYDOWN and e.key == K_ESCAPE):
self.runGame = False
break
if e.type==KEYDOWN:
if e.key==pygame.K_a: self.kl.append(1)
if e.key==pygame.K_d: self.kl.append(2)
if e.key==pygame.K_w: self.kl.append(3)
if e.key==pygame.K_s: self.kl.append(4)
if e.type==pygame.KEYUP:
if e.key==pygame.K_a: self.kl.remove(1)
if e.key==pygame.K_d: self.kl.remove(2)
if e.key==pygame.K_w: self.kl.remove(3)
if e.key==pygame.K_s: self.kl.remove(4)
if self.kl[-1:]==[1]: self.walk=[-self.speed, 0]
elif self.kl[-1:]==[2]: self.walk=[ self.speed, 0]
elif self.kl[-1:]==[3]: self.walk=[0,-self.speed]
elif self.kl[-1:]==[4]: self.walk=[0, self.speed]
else: self.walk=[0, 0]
self.x+=self.walk[0]
self.y+=self.walk[1]
def draw(self):
"""Draw and update the main screen"""
self.fps = self.clock.get_fps()
self.screen.fill(Color('purple'))
#print self.screen.get_rect()
#print player_rect
self.playerSpr.clamp_ip(self.screen.get_rect())
#pygame.draw.rect(self.screen, (255, 255, 255), self.playerrect)
self.screen.blit(self.playerSpr, (self.x,self.y), self.playerRec)
pygame.display.set_caption('Grid2. FPS: '+str(self.fps))
pygame.display.update()
game = Game()
game.mainLoop()
Why not use playerRec to keep track of the position of your player instead of the additional x and y attributes?
I suggest also using the move method (or move_ip):
def events(self):
for e in pygame.event.get():
...
self.playerRec.move_ip(*self.walk) # instead of self.x+=self.walk[0] / self.y+=self.walk[1]
def draw(self):
...
# probably do this right after 'move_ip'
self.playerRec.clamp_ip(self.screen.get_rect())
# note that 'blit' accepts a 'Rect' as second parameter
self.screen.blit(self.playerSpr, self.playerRec)
as a side note: You should consider using a Sprite, since it basically combines an Image and a Rect.
Two things:
You are not stopping movement of the sprite when it comes off the screen.
Make an move functions that will get a direction and will decide if it can move more to the side. That way when the right side of the sprite will be of screen, you will not move more to the right.
Since you put your direction keys in a list that works like a stack, you are only getting 1 direction per keypress. If you also want to move diagonally either make two lists one for both directions or use a easier method such as this:
if KEYDOWN == K_LEFT: direction_x = -1
if KEYUP == K_LEFT AND direction_x == -1: direction_x = 0
do this for every key.

Resources