Related
I'm trying to make a very simple game with my son in Kivy and Python. We are trying to make our viewport (camera) centered over the player as they move around our map that is self generating. We get the initial view, then as the player moves the initial chunk is shown in the correct place, but new chunks aren't being drawn at all.
By debugging, we can tell that we are creating chunks, that they have good values, and that our draw_chunks function knows to grab more chunks and to draw them. They just aren't being drawn. We think that our code for drawing the rectangles is probably wrong, but works for the initial load of the game. We've spent a couple hours trying to fix it. We've adjusted the viewport position a couple different ways as well as the rectangle code, but nothing seems to work. I'm hoping someone can point out what we missed. It is probably something very obvious or silly that we are overlooking. Does anyone have any ideas?
import kivy
import random
from kivy.app import App
from kivy.clock import Clock
from kivy.graphics import Color, Ellipse, Line, Rectangle
from kivy.uix.widget import Widget
from kivy.config import Config
from kivy.core.window import Window
import enum
# Constants for the chunk size and map dimensions
CHUNK_SIZE = 48
TILE_SIZE = 16
MAP_WIDTH = 256
MAP_HEIGHT = 256
#**************************
#* Tile Int Enum Class *
#**************************
class TileEnum(enum.IntEnum):
GRASS = 0
DIRT = 1
CONCRETE = 2
ASPHALT = 3
# Class to represent the game map
class GameMap:
def __init__(self, seed=None):
self.seed = seed
if seed is not None:
random.seed(seed)
self.chunks = {}
self.first_chunk = False
def generate_chunk(self, chunk_x, chunk_y):
# Use the RNG to generate the terrain for this chunk
terrain = []
for x in range(0, CHUNK_SIZE):
column = []
for y in range(0, CHUNK_SIZE):
column.append(random.randint(0, 3))
terrain.append(column)
return terrain
def get_chunk(self, chunk_x, chunk_y):
# Check if the chunk has already been generated
if (chunk_x, chunk_y) in self.chunks:
print("found it",chunk_x, chunk_y)
return self.chunks[(chunk_x, chunk_y)]
else:
# Generate the chunk and store it in the chunk cache
chunk = self.generate_chunk(chunk_x, chunk_y)
self.chunks[(chunk_x, chunk_y)] = chunk
print("made it",chunk_x,chunk_y)
return chunk
# Class to represent the player
class Player:
def __init__(self, pos=(0, 0)):
self.x, self.y = pos
self.speed = TILE_SIZE/2
def move_left(self):
self.x += self.speed
def move_right(self):
self.x -= self.speed
def move_up(self):
self.y -= self.speed
def move_down(self):
self.y += self.speed
class GameScreen(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.viewport_size = (TILE_SIZE*CHUNK_SIZE, TILE_SIZE*CHUNK_SIZE)
self.viewport_pos = (0, 0)
self.size = self.viewport_size
self.map = GameMap(seed=123)
self.player = Player((self.viewport_size[0]/2, self.viewport_size[1]/2))
self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
self._keyboard.bind(on_key_down=self._on_keyboard_down)
def draw_chunks(self):
# Determine the chunks that are currently in view
viewport_left = int(self.viewport_pos[0] // (CHUNK_SIZE * TILE_SIZE))
viewport_top = int(self.viewport_pos[1] // (CHUNK_SIZE * TILE_SIZE))
viewport_right = int((self.viewport_pos[0] + self.viewport_size[0]) // (CHUNK_SIZE * TILE_SIZE))
viewport_bottom = int((self.viewport_pos[1] + self.viewport_size[1]) // (CHUNK_SIZE * TILE_SIZE))
print(viewport_left, viewport_top, viewport_right, viewport_bottom)
# Iterate over the visible chunks and draw them
for x in range(viewport_left, viewport_right + 1):
for y in range(viewport_top, viewport_bottom + 1):
chunk = self.map.get_chunk(x, y)
#print(chunk)
for i in range(len(chunk)):
for j in range(len(chunk[i])):
if chunk[i][j] == TileEnum.GRASS:
# Draw a green square for grass
with self.canvas:
Color(0.25, 0.75, 0.25)
elif chunk[i][j] == TileEnum.DIRT:
# Draw a brown square for dirt
with self.canvas:
Color(0.75, 0.5, 0.25)
elif chunk[i][j] == TileEnum.CONCRETE:
# Draw a gray square for concrete
with self.canvas:
Color(0.5, 0.5, 0.75)
elif chunk[i][j] == TileEnum.ASPHALT:
# Draw a black square for asphalt
with self.canvas:
Color(0.25, 0.25, 0.5)
with self.canvas:
Rectangle(pos=(
(x * CHUNK_SIZE + i) * TILE_SIZE + self.viewport_pos[0],
(y * CHUNK_SIZE + j) * TILE_SIZE + self.viewport_pos[1]),
size=(TILE_SIZE, TILE_SIZE))
def draw_player(self):
# Draw a circle for the player
with self.canvas:
Color(0, 0.5, 0)
Ellipse(pos=(self.viewport_size[0]/2 - (TILE_SIZE/2), self.viewport_size[0]/2 - (TILE_SIZE/2)), size=(TILE_SIZE, TILE_SIZE))
def update(self, dt):
# Update the viewport position to keep the player centered
self.viewport_pos = (self.player.x - self.viewport_size[0]/2, self.player.y - self.viewport_size[1]/2)
print(self.viewport_pos)
# Redraw the chunks and player
self.canvas.clear()
self.draw_chunks()
self.draw_player()
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down=self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
#print(keycode)
if keycode[1] == 'left':
self.player.move_left()
elif keycode[1] == 'right':
self.player.move_right()
elif keycode[1] == 'up':
self.player.move_up()
elif keycode[1] == 'down':
self.player.move_down()
# Main application class
class ProceduralGenerationGameApp(App):
def build(self):
self.title = "Procedural Generation Game"
Config.set("graphics", "width", "768")
Config.set("graphics", "height", "768")
Config.set("graphics", "resizable", False)
Config.set("graphics", "borderless", False)
Config.set("graphics", "fullscreen", False)
Config.set("graphics", "window_state", "normal")
Config.set("graphics", "show_cursor", True)
Config.write()
window_width = Config.getint("graphics", "width")
window_height = Config.getint("graphics", "height")
# Create the game screen and schedule the update function to be called every frame
game_screen = GameScreen()
Window.size = (window_width, window_height)
Clock.schedule_interval(game_screen.update, 1)# 1.0 / 60.0)
return game_screen
if __name__ == "__main__":
ProceduralGenerationGameApp().run()
We updated the Rectangle code to this and reversed the players direction in his move functions:
with self.canvas:
x_chunk_offset = (x * CHUNK_SIZE * TILE_SIZE)
y_chunk_offset = (y * CHUNK_SIZE * TILE_SIZE)
x_tile_offset = (i * TILE_SIZE)
y_tile_offset = (j * TILE_SIZE)
actual_x = x_chunk_offset + x_tile_offset - self.viewport_pos[0]
actual_y = y_chunk_offset + y_tile_offset - self.viewport_pos[1]
Rectangle(pos=(actual_x, actual_y), size=(TILE_SIZE, TILE_SIZE))
I'm trying to create a game where the action is shown in a little box within the main screen object, freeing up the surrounding space for text and menus and what-not. Since the map is larger than the allotted window, I coded a basic "camera" that follows the player around. It mostly works, but I'm having trouble "trimming off" the area outside of this window.
Here's the relevant bits of code (EDITED to provide Working Example):
import pygame, os, sys
from pygame.locals import *
pygame.init()
RIGHT = 'RIGHT'
LEFT = 'LEFT'
UP = 'UP'
DOWN = 'DOWN'
class Camera():
def __init__(self, screen, x_ratio = 1, y_ratio = 1, x_offset = 0, y_offset = 0):
self.screen = screen.copy()
self.rec = self.screen.get_rect()
self.rec.width *= x_ratio
self.rec.height *= y_ratio
self.x_offset = x_offset
self.y_offset = y_offset
def get_pos(self):
return (self.x_offset - self.rec.x, self.y_offset - self.rec.y)
def get_window(self):
w = pygame.Rect(self.rec)
w.topleft = (0 - self.rec.x, 0 - self.rec.y)
return w
def move(self, x, y):
"""Move camera into new position"""
self.rec.x = x
self.rec.y = y
def track(self, obj):
while obj.rec.left < self.rec.left:
self.rec.x -= 1
while obj.rec.right > self.rec.right:
self.rec.x += 1
while obj.rec.top < self.rec.top:
self.rec.y -= 1
while obj.rec.bottom > self.rec.bottom:
self.rec.y += 1
class Map:
def __init__(self, width, height):
self.width = width
self.height = height
self.rec = pygame.Rect(0,0,self.width,self.height)
def draw(self, screen):
pygame.draw.rect(screen, (200,200,200), self.rec)
class Obj:
def __init__(self, char, x = 0, y = 0, width = 0, height = 0):
self.width = width
self.height = height
self.rec = pygame.Rect(x, y, width, height)
self.cur_map = None
self.timers = {}
#Dummying in chars for sprites
self.char = char
self.x_dir = 1
self.y_dir = 1
self.speed = 1
self.moving = False
def move(self):
if self.x_dir != 0 or self.y_dir != 0:
new_x = self.rec.x + (self.x_dir*self.speed)
new_y = self.rec.y + (self.y_dir*self.speed)
new_rec = pygame.Rect(new_x, new_y, self.width, self.height)
#Keep movement within bounds of map
while new_rec.left < self.cur_map.rec.left:
new_rec.x += 1
while new_rec.right > self.cur_map.rec.right:
new_rec.x -= 1
while new_rec.top < self.cur_map.rec.top:
new_rec.y += 1
while new_rec.bottom > self.cur_map.rec.bottom:
new_rec.y -= 1
self.rec = new_rec
def set_dir(self, d):
self.x_dir = 0
self.y_dir = 0
if d == LEFT:
self.x_dir = -1
elif d == RIGHT:
self.x_dir = 1
elif d == UP:
self.y_dir = -1
elif d == DOWN:
self.y_dir = 1
def set_moving(self, val = True):
self.moving = val
class Game:
def __init__(self):
self.screen_size = (800, 600)
self.screen = pygame.display.set_mode(self.screen_size)
self.map_screen = self.screen.copy()
self.title = 'RPG'
pygame.display.set_caption(self.title)
self.camera = Camera(self.screen, 0.75, 0.75)#, 10, 75)
self.fps = 80
self.clock = pygame.time.Clock()
self.debug = False
self.bg_color = (255,255,255)
self.text_size = 18
self.text_font = 'Arial'
self.text_style = pygame.font.SysFont(self.text_font, self.text_size)
self.key_binds = {LEFT : [K_LEFT, K_a], RIGHT : [K_RIGHT, K_d], UP : [K_UP, K_w], DOWN : [K_DOWN, K_s],
'interact' : [K_RETURN, K_z], 'inventory' : [K_i, K_SPACE], 'quit' : [K_ESCAPE]}
self.player = Obj('p', 0, 0, 10, self.text_size)
def draw(self, obj):
char = obj.char
self.draw_text(char, obj.rec.x, obj.rec.y, screen = self.map_screen)
def draw_text(self, text, x, y, color = (0,0,0), screen = None):
textobj = self.text_style.render(text, 1, color)
textrect = textobj.get_rect()
textrect.x = x
textrect.y = y
if screen == None:
"""Use default screen"""
self.screen.blit(textobj, textrect)
else:
screen.blit(textobj, textrect)
def play(self):
done = False
cur_map = Map(800, 800)
self.map_screen = pygame.Surface((cur_map.width, cur_map.height))
self.map_screen.fill(self.bg_color)
bg = pygame.Surface((cur_map.width, cur_map.height))
cur_map.draw(bg)
self.player.cur_map = cur_map
while not done:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key in self.key_binds[LEFT]:
self.player.set_dir(LEFT)
self.player.set_moving()
elif event.key in self.key_binds[RIGHT]:
self.player.set_dir(RIGHT)
self.player.set_moving()
elif event.key in self.key_binds[UP]:
self.player.set_dir(UP)
self.player.set_moving()
elif event.key in self.key_binds[DOWN]:
self.player.set_dir(DOWN)
self.player.set_moving()
elif event.type == KEYUP:
self.player.set_moving(False)
if self.player.moving:
self.player.move()
self.camera.track(self.player)
self.clock.tick()
self.screen.fill(self.bg_color)
self.map_screen.blit(bg, (0,0))
self.draw(self.player)
pygame.draw.rect(self.map_screen, (0,0,0), self.camera.rec, 1)
#self.screen.blit(self.map_screen, (0,0), [0 - self.camera.rec.x, 0 - self.camera.rec.y, self.camera.rec.width, self.camera.rec.height])
self.screen.blit(self.map_screen, self.camera.get_pos(), self.camera.get_window())
pygame.display.flip()
game = Game()
game.play()
Moving the player past past the bounds of the camera's window causes the window to roll up completely and disappear. I tried adjusting the blitting coordinates, as advised earlier, but it seems to only change the direction in which the window rolls up.
From your updated code, the blitting coordinates for self.screen.blit(...) are still changing: self.camera.get_window() changes value because rec.x and rec.y are values referring to the player position within the map. Hence you should define a constant minimap coordinate, this should be the same as the camera offset.
self.screen.blit(self.map_screen, (self.camera.x_offset,self.camera.y_offset), (*self.camera.get_pos(), self.camera.rec.width, self.camera.rec.height))
Change the Camera().get_pos() to:
def get_pos(self):
return (self.rec.x, self.rec.y)
I believe I only changed the self.screen.blit(...) and stopped using or rewrote your Camera functions as you're confusing yourself with all the rec variables.
To illustrate it working amend the Map().draw(screen) to:
def draw(self, screen):
pygame.draw.rect(screen, (200,200,200), self.rec)
pygame.draw.circle(screen, (255, 255, 255), (50, 50), 20, 2)
One tip as well don't draw the entire map at each loop, just the part that will be visible.
This is my first time asking here, so sorry if I don't ask very well.
Lately I've been trying to make a Terraria-type game where you can break/place blocks in a randomly generated landscape. While trying to implement the breaking-block mechanic (which is triggered by clicking on a block), I ran into an issue. The block no longer became solid (that's a good thing), but the block's image is still there, and I can walk right through it.
A visual example:
Before breaking block vs.
After breaking block
Here's the code
Main file (hopefully only the relevant bits):
# Imports
import pygame as pg
import json
import sys
import random
import os
from settings import *
from world_handler import *
# Initialize pygame
pg.mixer.pre_init()
pg.init()
# Fonts
# NOTE: put fonts in here
# Helper functions
def load_image(file_path):
# Loads an image
img = pg.image.load(file_path)
return img
def play_sound(sound, loops=0, maxtime=0, fade_ms=0):
# Plays some audio
if sound_on:
sound.play(loops, maxtime, fade_ms)
def play_music():
# Plays background music
if sound_on:
pg.mixer.music.play(-1)
# File paths
current_path = os.path.dirname(__file__)
assets_path = os.path.join(current_path, "assets")
image_path = os.path.join(assets_path, "img")
# Images
player_standing = load_image((os.path.join(image_path, "player", "standing", "player-standing.png")))
player_walking1 = load_image((os.path.join(image_path, "player", "walking", "player-walking1.png")))
player_walking2 = load_image((os.path.join(image_path, "player", "walking", "player-walking2.png")))
player_walking3 = load_image((os.path.join(image_path, "player", "walking", "player-walking3.png")))
player_walking4 = load_image((os.path.join(image_path, "player", "walking", "player-walking4.png")))
player_jumping = load_image((os.path.join(image_path, "player", "jumping", "player-jumping.png")))
player_images = {"walking": [player_walking1, player_walking2, player_walking3, player_walking4],
"jumping": player_jumping,
"standing": player_standing}
block_images = {"Grass": load_image((os.path.join(image_path, "blocks", "grass.png"))),
"Dirt": load_image((os.path.join(image_path, "blocks", "dirt.png"))),
"Stone": load_image((os.path.join(image_path, "blocks", "stone.png")))}
cursor_tracker = load_image((os.path.join(image_path, "misc", "clear-single-pixel.png")))
class Entity(pg.sprite.Sprite):
def __init__(self, x, y, image):
# Initialize an entity
super().__init__()
self.image = image
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.vx = 0
self.vy = 0
def apply_gravity(self, world):
# Let the enemy be affected by gravity
self.vy += world.gravity
self.vy = min(self.vy, world.terminal_velocity)
class Block(Entity):
def __init__(self, x, y, image):
# Initialize the block
super().__init__(x, y, image)
class Cursor(Entity):
def __init__(self, x, y, image):
# Initialize the invisible mouse cursor object
# This will be used to track where the mouse goes and if the mouse is on a block
super().__init__(x, y, image)
self.on_block = False
def follow_mouse(self):
# Make object follow the mouse
self.mouse_x, self.mouse_y = pg.mouse.get_pos()
self.rect.x = self.mouse_x
self.rect.y = self.mouse_y
def detect_block_collision(self, world):
# Detects collsion between cursor tracker and a block
hit_list = pg.sprite.spritecollide(self, world.blocks, True)
if len(hit_list) > 0:
pass
def update(self, world):
# Update the cursor object
self.follow_mouse()
world.active_sprites.add(self)
class Player(Entity):
def __init__(self, images):
# Initialize the player
super().__init__(0, 0, images["standing"])
# Images in each direction
self.image_standing_right = images["standing"]
self.image_standing_left = pg.transform.flip(self.image_standing_right, 1, 0)
self.images_walking_right = images["walking"]
self.images_walking_left = [pg.transform.flip(img, 1, 0) for img in self.images_walking_right]
self.image_jumping_right = images["jumping"]
self.image_jumping_left = pg.transform.flip(self.image_jumping_right, 1, 0)
# Player variables
self.running_images = self.images_walking_right
self.image_index = 0
self.steps = 0
self.speed = 3.5
self.jump_power = 12
self.vx = 0
self.vy = 0
self.direction = "right"
self.on_ground = True
self.score = 0
self.health = 100
self.max_health = 100
self.invincibility = 0
def move_left(self):
# Move to the left
self.vx = -self.speed + 0.9
self.direction = "left"
def move_right(self):
# Move to the rightS
self.vx = self.speed
self.direction = "right"
def stop(self):
# Stop it right there
self.vx = 0
def jump(self, blocks):
# Jump up, jump up, and get down
self.rect.y += 1
hit_list = pg.sprite.spritecollide(self, blocks, False)
if len(hit_list) > 0:
self.vy = -1 * self.jump_power
self.rect.y -= 1
def check_world_boundaries(self, world):
# Make sure the player doesn"t walk off the world
if self.rect.left < 0:
self.rect.left = 0
elif self.rect.right > world.width:
self.rect.right = world.width
def move_and_process_blocks(self, blocks):
# Detect block collisions
# Block side collisions
self.rect.x += self.vx
hit_list = pg.sprite.spritecollide(self, blocks, False)
for block in hit_list:
if self.vx > 0:
self.rect.right = block.rect.left
self.vx = 0
elif self.vx < 0:
self.rect.left = block.rect.right
self.vx = 0
self.on_ground = False
# Block top and bottom collisions
self.rect.y += self.vy + 1 # The +1 isn"t necessary, but it helps
hit_list = pg.sprite.spritecollide(self, blocks, False)
for block in hit_list:
if self.vy > 0:
self.rect.bottom = block.rect.top
self.on_ground = True
self.vy = 0
elif self.vy < 0:
self.rect.top = block.rect.bottom
self.on_ground = True
self.vy = 0
def set_image(self):
# Set images and animate
if self.on_ground:
if self.vx != 0:
if self.direction == "right":
self.walking_images = self.images_walking_right
elif self.direction == "left":
self.walking_images = self.images_walking_left
self.steps = (self.steps + 1) % self.speed
if self.steps == 0:
self.image_index = (self.image_index + 1) % len(self.walking_images)
self.image = self.walking_images[self.image_index]
else:
if self.direction == "right":
self.image = self.image_standing_right
elif self.direction == "left":
self.image = self.image_standing_left
else:
if self.direction == "right":
self.image = self.image_jumping_right
elif self.direction == "left":
self.image = self.image_jumping_left
def die(self):
# D E D
pass
def check_block_breaks(self, blocks):
# Break a block
# mouse_pos = pg.mouse.get_pos()
# for block in blocks:
# if block.rect.collidepoint(mouse_pos):
# print("hi")
pass
def respawn(self, world):
# Hey, you"re back!
self.rect.x = world.start_x
self.rect.y = world.start_y
self.health = self.max_health
self.invincibility = 0
self.direction = "right"
def update(self, world):
# Constantly update the player
self.apply_gravity(world)
self.move_and_process_blocks(world.blocks)
self.check_world_boundaries(world)
self.set_image()
self.check_block_breaks(world.blocks)
if self.health > 0:
if self.invincibility > 0:
self.invincibility -= 1
else:
self.die()
class Game():
def __init__(self):
# Initialize the game itself
self.window = pg.display.set_mode([WINDOWWIDTH, WINDOWHEIGHT])
pg.display.set_caption(TITLE)
self.clock = pg.time.Clock()
self.done = False
self.reset()
def start(self):
# Start the whole thing up
self.world = World(worlds[self.current_world])
self.cursor = Cursor(0, 0, cursor_tracker)
self.world.reset()
self.player.respawn(self.world)
def reset(self):
# Reset the game
self.player = Player(player_images)
self.current_world = 0
self.start()
def update(self):
# Update things in the game
self.player.update(self.world)
self.cursor.update(self.world)
if self.player.health <= 0:
self.player.respawn(self.world)
def calculate_offset(self):
# Calculate x/y coordinates after screen scrolls
x = -1 * self.player.rect.centerx + WINDOWWIDTH / 2
if self.player.rect.centerx < WINDOWWIDTH / 2:
x = 0
elif self.player.rect.centerx > self.world.width - WINDOWWIDTH / 2:
x = -1 * self.world.width + WINDOWWIDTH
y = -1 * self.player.rect.centery + WINDOWHEIGHT / 2
if self.player.rect.centery < WINDOWHEIGHT / 2:
y = 0
elif self.player.rect.centery > self.world.height - WINDOWHEIGHT / 2:
y = -1 * self.world.height + WINDOWHEIGHT
return x, y
def draw(self):
# Draw sprites to the screen
self.offset_x, self.offset_y = self.calculate_offset()
self.world.active_layer.fill(TRANSPARENT)
self.world.active_sprites.draw(self.world.active_layer)
if self.player.invincibility % 3 < 2:
self.world.active_layer.blit(self.player.image, [self.player.rect.x, self.player.rect.y])
self.window.blit(self.world.background_layer, [self.offset_x / 3, self.offset_y])
self.window.blit(self.world.inactive_layer, [self.offset_x, self.offset_y])
self.window.blit(self.world.active_layer, [self.offset_x, self.offset_y])
self.offset_cursor_x = self.cursor.rect.x - self.offset_x
self.offset_cursor_y = self.cursor.rect.y - self.offset_y
self.cursor.rect.x = self.offset_cursor_x
self.cursor.rect.y = self.offset_cursor_y
pg.display.update(0, 0, WINDOWWIDTH, WINDOWHEIGHT)
def process_events(self):
# Handle events (key presses, mouse clicks, etc)
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
elif event.type == pg.KEYDOWN:
# Jump
if event.key == JUMP:
self.player.jump(self.world.blocks)
# Debug reset
elif event.key == pg.K_r:
self.reset()
# Debug close
elif event.key == pg.K_q:
self.done = True
# Break a block if you click on it
if event.type == pg.MOUSEBUTTONDOWN:
# for block in self.world.blocks:
# if block.rect.collidepoint(self.offset_cursor_x, self.offset_cursor_y):
# block.kill()
self.cursor.detect_block_collision(self.world)
pressed = pg.key.get_pressed()
if pressed[LEFT]:
self.player.move_left()
elif pressed[RIGHT]:
self.player.move_right()
else:
self.player.stop()
def loop(self):
# Loop through essential functions
while not self.done:
self.process_events()
self.update()
self.draw()
self.clock.tick(FPS)
if __name__ == "__main__":
# Begin the loop and pre-initialize the game
game = Game()
game.start()
game.loop()
pg.quit()
sys.exit()
World Handler (generated blocks and adds them to groups):
Some parts are commented out because they serve no purpose yet, but will soon.
# Imports
import pygame as pg
import json
import random
import os
from settings import *
from main import *
# Initialize pygame
pg.init()
class World():
def __init__(self, file_path):
# Initialize the world
# Starting entities
self.starting_blocks = []
# Entity groups
self.blocks = pg.sprite.Group()
# Sprite groups (active/inactive)
self.active_sprites = pg.sprite.Group()
self.inactive_sprites = pg.sprite.Group()
# Read the world json file
with open(file_path, "r") as f:
data = f.read()
map_data = json.loads(data)
# World width and height
self.width = map_data["width"] * GRID_SIZE
self.height = map_data["height"] * GRID_SIZE
# Player start position
self.start_x = map_data["start"][0] * GRID_SIZE
self.start_y = map_data["start"][1] * GRID_SIZE
# Load blocks
for item in map_data["blocks"]:
x, y = item[0] * GRID_SIZE, item[1] * GRID_SIZE
img = block_images[item[2]]
self.starting_blocks.append(Block(x, y, img))
# Layers
self.background_layer = pg.Surface([self.width, self.height], pg.SRCALPHA, 32)
self.inactive_layer = pg.Surface([self.width, self.height], pg.SRCALPHA, 32)
self.active_layer = pg.Surface([self.width, self.height], pg.SRCALPHA, 32)
# Load background color
if map_data["bg-color"] != "":
self.background_layer.fill(map_data["bg-color"])
# Load background image
# if map_data["bg-image"] != "":
# bg_image = pg.image.load(map_data["bg-image"]).convert_alpha()
#
# if map_data["bg-fill-y"]:
# h = bg_image.get_height()
# w = int(bg_image.get_width() * WINDOWHEIGHT / h)
# bg_image = pg.transform.scale(bg_image, (w, WINDOWHEIGHT))
#
# if "top" in map_data["bg-position"]:
# start_y = 0
# elif "bottom" in map_data["bg-postion"]:
# start_y = self.height = bg_image.get_height()
#
# if map_data["bg-repeat-x"]:
# for x in range(0, self.width, bg_image.get_width()):
# self.background_layer.blit(bg_image, [x, start_y])
# else:
# self.background_layer.blit(bg_image, [0, start_y])
# Load background music
# pg.mixer.music.load(map_data["music"])
# Set the world's gravity strength and terminal velocity
self.gravity = map_data["gravity"]
self.terminal_velocity = map_data["terminal-velocity"]
# Grass generator
if map_data["gen-type"] == "earth":
gen_loop = map_data["width"]
x = 0
y = 56 * GRID_SIZE # The general y coordinate for block placing
y_gen = 56 * GRID_SIZE # Stored to be referenced in order to make a smoother landscape
for i in range (0, map_data["width"]):
# Generate grass
img = block_images["Grass"]
self.starting_blocks.append(Block(x, y, img))
y += GRID_SIZE
# Generate dirt
for i in range(0, 6):
img = block_images["Dirt"]
self.starting_blocks.append(Block(x, y, img))
y += GRID_SIZE
# Generate stone
for i in range(1, int((self.height / GRID_SIZE))):
img = block_images["Stone"]
self.starting_blocks.append(Block(x, y, img))
y += GRID_SIZE
y = y_gen
x += GRID_SIZE
gen_loop -= 1
# Randomly decide what the next grass' y will be in relation to the previous one
random_grass = random.randint(0, 5)
# The lowest point you'll find a block of grass
lowest_grass_y = 53 * GRID_SIZE
# How extreme the changes in block heights will be
# 0 is flat, 1 will have pretty smooth terrain, while something like 10 would be super steep
gen_extremity = 1
# Keep the grass at the same y
if random_grass == 0 or random_grass == 1 or random_grass == 2 or random_grass == 3:
gen_loop -= 1
if y <= lowest_grass_y:
y += GRID_SIZE
# Increase y
elif random_grass == 4:
y_gen += GRID_SIZE * gen_extremity
if y <= lowest_grass_y:
y += GRID_SIZE
# Decrease y
elif random_grass == 5:
y_gen -= GRID_SIZE * gen_extremity
if y <= lowest_grass_y:
y += GRID_SIZE
else:
raise ValueError("How did we get here? Grass generator somehow generated an invalid number.")
# Add starting entities to their groups
self.blocks.add(self.starting_blocks)
# Add sprites to inactive/active sprite groups
self.inactive_sprites.add(self.blocks)
# Does... something?
for s in self.active_sprites:
s.image.convert()
for s in self.inactive_sprites:
s.image.convert()
# Draw inactive sprites to the inactive layer
self.inactive_sprites.draw(self.inactive_layer)
# Convert layers
self.background_layer.convert()
self.inactive_layer.convert()
self.active_layer.convert()
Setting file (to help you recreate the issue and find out what's going on
# Imports
import pygame as pg
import os
# File paths
current_path = os.path.dirname(__file__)
assets_path = os.path.join(current_path, 'assets')
image_path = os.path.join(assets_path, 'img')
# Window settings
TITLE = "Mooncraft"
WINDOWWIDTH = 960
WINDOWHEIGHT = 640
FPS = 60
GRID_SIZE = 32
# Options
sound_on = True
# Controls
LEFT = pg.K_a
RIGHT = pg.K_d
JUMP = pg.K_SPACE
# Colors
TRANSPARENT = (0, 0, 0, 0)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# World files
worlds = [(os.path.join(assets_path, 'worlds', 'earth.json'))]
If someone could explain what I'm doing wrong, it would be much appreciated.
Update 2: Added player class and settings file to help anyone willing to assist me find the issue.
From just a glance, it looks like you might not be clearing the screen at any point in the update loop, but rather drawing over what was already there. This would result in the block still being visible, but not actually there. Try adding screen.fill(#Color here) before flipping the display. Also try using pygame.display.update() instead of pygame.display.flip()
You can do this:
all_sprites = pg.sprite.Group()
all_sprites.add(your_sprite)
#your event:
all_sprites.remove(your_sprite)
im trying to make a simple game where the there are soldiers coming towards you and when you click them to "kill" them they go at the back of the screen and start to come towards you, so on.....
however i'm having trouble with pygame mouse click event and it just doesnt work.
heres my code so far:
import pygame, math
from random import randrange
import sys, math, pygame
from operator import itemgetter
def getKey(customobj):
return customobj.getKey()
class Point3D:
def __init__(self, imfiles, nfrm, x = 0, y = 0, z = 0):
self.x, self.y, self.z = float(x), float(y), float(z)
self.frms = []
self.nfrm=nfrm
self.index=0
for k in range(0,nfrm):
im=pygame.image.load(imfiles+'_'+str(k+1)+'.png')
im.set_colorkey((0,0,0))
self.frms.append(im)
def
project(self, win_width, win_height, fov, viewer_distance):
""" Transforms this 3D point to 2D using a perspective projection. """
factor = fov / (viewer_distance + self.z)
x = self.x * factor + win_width / 2
y = -self.y * factor + win_height / 2
return Point3D(x, y, self.z)
def draw3D(self, wsurface, fov, viewer_distance, max_depth):
win_width=wsurface.get_width()
win_height=wsurface.get_height()
factor = fov / (viewer_distance + self.z)
x = self.x * factor + win_width / 2
y = -self.y * factor + win_height / 2
size = int((1 - float(self.z) / max_depth) * 64)
im=pygame.transform.smoothscale(self.frms[self.index],(size,size))
try:
wsurface.blit(im, (x, y))
except:
print((x,y))
self.index=self.index+1
if self.index >= self.nfrm:
self.index=0
def getKey(self):
return -self.z
class StartField:
def __init__(self, num_stars, max_depth):
pygame.init()
myWin = pygame.display.set_mode((640, 450), 0, 32)
pygame.display.set_caption('Drawing')
self.screen = myWin.subsurface([0,0,640,400]);
self.txtwin = myWin.subsurface([0,400,640,50]);
pygame.display.set_caption("Task C")
self.clock = pygame.time.Clock()
self.num_stars = num_stars
self.max_depth = max_depth
self.init_stars()
def init_stars(self):
""" Create the starfield """
self.stars = []
for i in range(self.num_stars):
# A star is represented as a list with this format: [X,Y,Z]
star = Point3D('im',8,randrange(-25,25), randrange(-25,25), randrange(1, self.max_depth))
self.stars.append(star)
def move_and_draw_stars(self):
""" Move and draw the stars """
origin_x = self.screen.get_width() / 2
origin_y = self.screen.get_height() / 2
stars=sorted(self.stars,key = getKey)
for star in stars:
# The Z component is decreased on each frame.
star.z -= 0.05
# If the star has past the screen (I mean Z<=0) then we
# reposition it far away from the screen (Z=max_depth)
# with random X and Y coordinates.
if star.z <= 0:
star.x = randrange(-25,25)
star.y = randrange(-25,25)
star.z = self.max_depth
# Convert the 3D coordinates to 2D using perspective projection.
star.draw3D(self.screen, 128, 0, self.max_depth)
def run(self):
""" Main Loop """
bgPicture = pygame.transform.smoothscale(pygame.image.load('Starfield.jpg'),(self.screen.get_width(),self.screen.get_height()))
font = pygame.font.Font(None, 36)
while 1:
# Lock the framerate at 50 FPS.
self.clock.tick(50)
# Handle events.
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
return
self.screen.blit(bgPicture, [0,0])
self.move_and_draw_stars()
# Text window outputs
self.txtwin.fill([200,200,200])
text = font.render("Total Score: ", 1, (10, 10, 10))
self.txtwin.blit(text, [5, 5])
pygame.display.update()
if __name__ == "__main__":
StartField(256, 24).run()
pygame.init()
pygame.mixer.init()
sounda= pygame.mixer.Sound("MuseUprising.mp3")
sounda.play()
To test for the left mouse button:
if event.type == pygame.MOUSEBUTTONDOWN and pygame.mouse.get_pressed()[0]:
If give the soldier a pygame.Rect you can use that to check for collision with the mouse pointer like this:
mouse_pos = pygame.mouse.get_pos()
if event.type == pygame.MOUSEBUTTONDOWN and pygame.mouse.get_pressed()[0] and self.rect.collidepoint(mouse_pos):
This question already has answers here:
Why it doesn't spin in a circle? And how to fix it?
(1 answer)
Ship moves up and left faster than down and right when rotating in pygame
(1 answer)
Closed 2 years ago.
This question is related to
My code is below. You can use any small image for my images.
import sys, os, pygame, itertools
from math import sin,cos,pi, radians
from pygame.locals import *
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (50,50) #Set window position
pygame.init()
clock = pygame.time.Clock()
FPS = 1000
SCREENW = 800 #screen width
SCREENH = 740 #screen height
BLACK = (0, 0, 0)
BLUE = (0, 0, 255)
ORANGE = (128, 100, 30)
FONT1= "Cookie-Regular.ttf"
SCREEN = pygame.display.set_mode((SCREENW, SCREENH), 0, 32) #display screen
clock = pygame.time.Clock()
#-------------------------------------------------------------------------------
def maketext(msg,fontsize, colour = ORANGE, font = FONT1):
mafont = pygame.font.Font(font, fontsize)
matext = mafont.render(msg, True, colour)
matext = matext.convert_alpha()
return matext
#-------------------------------------------------------------------------------
def print_info():
""""""
textcos = maketext(str(round(obj.rect.x, 2)) + " " + str(round(obj.rect.y, 2)), 30)
SCREEN.blit(textcos, (obj.rect.x, obj.rect.y + 30))
#-------------------------------------------------------------------------------
class object_factory(pygame.sprite.Sprite):
def __init__(self, imagelist, xpos, ypos, speedx = 0, speedy = 0, value = 0):
"""Constructor"""
pygame.sprite.Sprite.__init__(self)
self.name = ""
self.frame = 0
self.imagelist = imagelist
self.image = imagelist[self.frame]
self.mask = pygame.mask.from_surface(self.image) # pixelmask
self.rect = self.image.get_rect()
self.rect.x = xpos
self.rect.y = ypos
#self.speedx = speedx
#self.speedy = speedy
self.timer = 0
self.timerlimit = 10
#----------------------------------------------------------------------
#def move(self): # wallsprites, Herosprite, looptime
#self.rect.x += self.speedx
#self.rect.y += self.speedy
#----------------------------------------------------------------------
def update(self):
""""""
self.image = self.imagelist[self.frame]
if self.timer >= self.timerlimit:
self.frame += 1
if self.frame >= len(self.imagelist):
self.frame = 0
self.timer = 0
self.timer += 1
plat = pygame.image.load("plt0.png").convert_alpha()
star = pygame.image.load("gemp0.png").convert_alpha()
#box = pygame.image.load("crateB.png").convert_alpha()
platforms = pygame.sprite.Group()
boxes = pygame.sprite.Group()
rotcenx = SCREENW/2
rotceny = SCREENH/2
radius = 200
angle = radians(90) #pi/4 # starting angle 45 degrees
omega = radians(5) #Angular velocity
m = rotcenx + radius * cos(angle) #Starting position x
n = rotceny - radius * sin(angle) #Starting position y
for _ in itertools.repeat(None, 1):
madyax = SCREENW/2
madyay = SCREENH/2
araya = 200
konaya = radians(180) #pi/4 # starting angle 45 degrees
konika_pravegaya = radians(5) #Angular velocity
a = madyax + (araya * cos(konaya)) #Starting position x
b = madyay - (araya * sin(konaya)) #Startinh position y
plat = object_factory([plat], a, b)
plat.araya = araya
plat.konaya = konaya
plat.kp = konika_pravegaya
platforms.add(plat)
while True:
ms = clock.tick(FPS) # milliseconds passed since last frame
#looptime = milliseconds / 1000.0 # seconds passed since last frame
SCREEN.fill((BLACK))
pygame.draw.circle(SCREEN, BLUE, (SCREENW / 2, SCREENH / 2), 5)
##-----------------------------------------------------------
SCREEN.blit(star, (m, n)) # Draw current x,y
angle = angle + omega # New angle, we add angular velocity
m = m + radius * omega * cos(angle + pi / 2) # New x
n = n - radius * omega * sin(angle + pi / 2) # New y
##-----------------------------------------------------------
# show object anchored to center of rotation
pygame.draw.line(SCREEN, ORANGE, (rotcenx, rotceny), (m, n))
text = maketext(str(radius), 30)
SCREEN.blit(text, (m, n - 40))
text = maketext((str(round(m, 2)) + " " + str(round(n, 2))), 30)
SCREEN.blit(text, (m, n + 40)) # Draw current x,y
##------------------------------------------------------------------
for plat in platforms:
plat.konaya = plat.konaya + plat.kp
plat.rect.x = plat.rect.x + plat.araya * plat.kp * cos(plat.konaya + pi / 2)
plat.rect.y = plat.rect.y - plat.araya * plat.kp * sin(plat.konaya + pi / 2)
##------------------------------------------------------------------------
pygame.draw.line(SCREEN, ORANGE, (madyax, madyay), (plat.rect.x, plat.rect.y))
platforms.update()
platforms.draw(SCREEN)
pygame.event.pump()
keys = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
pygame.display.update()
pygame.time.wait(100)
Why does the code work when used outside a class and NOT when in? I simply can't find what I have done wrong.
Please don't ask me to read any Google documents or search on the Internet as I am posting after doing so and NOT finding an answer to my question. I am NOT an expert in math and would only like to know a solution to this problem. Please help.
Link to video is below
http://youtu.be/0oRDX246aj8