How would I go about achieving scrolling text in pygame? As the Text class is derived from Sprite, I thought I would be able to wrap it in the usual way I would a Sprite object. Instead, it simply disappears off the left edge of the screen, never to return (I can't get it to bounce off each side either).
My aim with this program is to reinforce things I've just covered in some educational material. However, scrolling text wasn't one of them (would look cool though).
Thanks,
Dave
Code(specific block is under comment 'add scrolling text at bottom of screen'):
# Simon Says
# Displays sequences of sounds and/or colors which the user must repeat
from livewires import games, color
import random
# initialise screen
games.init(screen_width = 640, screen_height = 480, fps = 50)
class Game(object):
"""A sequence repeat game"""
def __init__(self):
"""Initialize Game object"""
# create key
self.menu_title = games.Text(value = "Key",
size = 25,
color = color.red,
top = 5,
left = games.screen.width - 100,
is_collideable = False)
self.menu_choice0 = games.Text(value = "0 - Quit",
size = 20,
color = color.red,
top = self.menu_title.bottom + 5,
left = games.screen.width - 120,
is_collideable = False)
self.menu_choice1 = games.Text(value = "1 - Yellow",
size = 20,
color = color.red,
top = self.menu_choice0.bottom + 5,
left = games.screen.width - 120,
is_collideable = False)
self.menu_choice2 = games.Text(value = "2 -Thrust sound",
size = 20,
color = color.red,
top = self.menu_choice1.bottom + 5,
left = games.screen.width - 120,
is_collideable = False)
games.screen.add(self.menu_title)
games.screen.add(self.menu_choice0)
games.screen.add(self.menu_choice1)
games.screen.add(self.menu_choice2)
# add scrolling text at bottom of screen
self.instructions = games.Text(value = "Repeat the sequence by entering the "
"corresponding number.",
size = 20,
color = color.green,
x = games.screen.width/2,
bottom = games.screen.height - 2,
dx = - 0.75)
games.screen.add(self.instructions)
# wrap
if self.instructions.left < 0:
self.instructions.left = games.screen.width
def play(self):
"""Play game"""
# load and set background
black_background = games.load_image("blackbackground.jpg", transparent = False)
games.screen.background = black_background
games.screen.mainloop()
def main():
sequence = Game()
sequence.play()
main()
I don't see anything that would handle it all for you in livewires. You could probably use livewires.Object with some custom code. Build your surface like livewires.game.Text does then, move it around. Or even make it tickable and keep track of what part of the text should be shown and blit just that part to the object surface.
Related
I have created a 3d button in ursina, but suppose we are a kilometer away from the button, we can still press it. Where is the logic in it? I would like to make the button clickable only when in a certain radius from it.
horror_gamemode = Button(parent = scene, model = 'cube', texture = None, color = color.black, highlight_color = color.dark_gray, scale = 1, position = (3, -49, 4), collider = 'mesh')
If I got it right, you want to make a button not clickable when we are over, lets say, 100 meters. For that you can use ursina's distance function to calculate the position between the camera and the button and if it is less that 100 meters, then make it clickable, otherwise unclickable (you can use .disabled = False # or True).
Example:
from ursina import *
from ursina.prefabs import *
app = Ursina()
horror_gamemode = Button(parent = scene, model = 'cube', texture = None, color = color.black, highlight_color = color.dark_gray, scale = 1, position = (0, 0, 0), collider = 'mesh')
def some_random_func():
print("HI")
def update():
if (distance(camera, horror_gamemode) < 50):
button.color = color.green
button.disabled = False
horror_gamemode.on_click = some_random_func
else:
button.color = color.red
print(distance(horror_gamemode, camera))
button.disabled = True
horror_gamemode.on_click = None
EditorCamera()
app.run()
I am currently making a game with Ursina and i've been struggling to find a code to make a Main-Starting-Screen to start the game or show controlls.
Here is my game code:
game = Ursina()
ground = Entity(model = "untitled.blend", texture = "Soul Sand.jpg", scale = 400, collider = "mesh")
player = FirstPersonController(model = "player.blend", position = (-6.39844, 148.284, 62.5471),jump_height = 7, jump_duration = 0.5, speed = 10, texture = "Player Texture.jpg", collider = "mesh")
camera.z = None
health_bar = HealthBar(bar_color=color.lime.tint(-.25), roundness=.5, value=100)
def input(key):
if key == "shift":
player.speed = 25
elif key == "control":
player.speed = 10
house = Entity(model = "House.blend", texture = "Tree Texture.jpg", scale = 2, collider = "mesh", position = (0, 146.3, 15))
tree = Entity(model = "Tree.blend", scale = 15, texture = "Tree Texture.jpg", collider = "mesh", position = (15.5488, 140, 55.0674))
game.run()
You could try just displaying a quad model which is a 2d square
Menuback=Entity(model="quad"scale=(10,10))
and just make
Allother entities to .enabled=False
And the menu to true
Create a button
And onclick menuback.enabled =False
And the rest od entities set on true
Here is the ursina docs if u need to look at some stuff
https://www.ursinaengine.org/api_reference.html
I am a young Python programmer. I decided to create a 2D game using pygame Purpose of the game: Drive as much distance as possible on the car, without crashing into objects that will "spawn" during the game. The car will drive across the field.
I have problems with decoration sprites (during the game, trees will "fall" down on the edges of the window) Fig1.pic1
So, trees should spawn right after the previous ones reach the middle of the window, but when new trees spawn, this is what happens to me: Fig2. And the game starts to freeze pic2
Here is my code:
from superwires import games, color
from random import randrange
games.init(screen_width = 530, screen_height = 600, fps = 60)
#Car sprite
class Car(games.Sprite):
image = games.load_image("C:/python/car.bmp")
def __init__(self):
super(Car, self).__init__(image = Car.image,
x = games.mouse.x,
bottom = games.screen.height - 10)
self.score = games.Text(value = 0, size = 25, color = color.yellow,
top = 5, right = games.screen.width/2)
games.screen.add(self.score)
def update(self):
self.x = games.mouse.x
if self.left < 65:
self.left = 65
if self.right > games.screen.width - 65:
self.right = games.screen.width - 65
#Tree sprite
class Bush1(games.Sprite):
image = games.load_image("C:/python/bush.bmp")
speed = 1
def __init__(self, x = 20, y = 100):
super(Bush1, self).__init__(image = Bush1.image,
x = x, y = y,
dy = Bush1.speed)
def update(self):
if self.bottom > games.screen.height/2:
newbush = Bush1()
newbush.__init__(x = 20, y = -100)
games.screen.add(newbush)
class Bush2(games.Sprite):
image = games.load_image("C:/python/bush.bmp")
speed = 1
def __init__(self, x = 515, y = 100):
super(Bush2, self).__init__(image = Bush2.image,
x = x, y = y,
dy = Bush2.speed)
#Spawning new trees
def update(self):
if self.bottom > games.screen.height/2:
newbush = Bush2()
newbush.__init__(x = 515, y = -100)
games.screen.add(newbush)
#Start
def main():
road = games.load_image("road.jpg", transparent = False)
games.screen.background = road
bush1 = Bush1()
bush2 = Bush2()
car = Car()
games.screen.add(bush1)
games.screen.add(bush2)
games.screen.add(car)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
main()
I'll be glad to know where I made a mistake.
Used: Python 3.9, superwires, games
Here's your problem. Once your first bush reaches the halfway point, you create two new bushes ON EVERY FRAME. That's what you're seeing in your pic2 -- you have hundreds of slightly overlapping bushes. You need to create a new bush only when the old one is EXACTLY at the halfway point, not AT OR BELOW the halfway point:
if self.bottom == games.screen.height//2:
You might also consider deleting bushes once they fall off the bottom
This may not address the specific problem you're running across, but it might help to unpack and zero in on what is happening.
Here you are creating a new Bush1 and Bush2 object:
bush1 = Bush1()
bush2 = Bush2()
Note that the code for these two classes are virtually identical. The differences are only in the init defaults. It is equivalent to doing this:
bush1 = Bush1()
bush2 = Bush1(515, 100)
It seems you have a misunderstanding, based on this, about how classes in Python work. This is reinforced by the two almost-equivelent-but-for-constants update blocks:
def update(self):
if self.bottom > games.screen.height/2:
newbush = Bush1()
newbush.__init__(x = 20, y = -100)
games.screen.add(newbush)
On the third line of these blocks you're creating a new object. Then you're re-calling that objects __init__ method. When you create a new object, a couple of hidden 'private' methods are run, the last of which is the __init__ method. Typically if you need instantiation of an object to change, you would alter that method. You should never need to re-call that method.
Finally, you are not, so far as I can tell, actually moving the bushes. I suspect when you say 'starts to freeze' what is happening is you have many instantiations of the bushes overlapping one another, and the game is taking longer and longer to process each loop. Putting a print statement in each init method would probably help identify this problem, but it's possible your frameworks might have a better way to do that. (I am not familiar with them.)
Refactoring a bit to clean up the usage of classes and scoping variables properly should yield something along the lines of this (though almost certainly there is more we could do):
from superwires import games, color
from random import randrange
games.init(screen_width = 530, screen_height = 600, fps = 60)
#Car sprite
class Car(games.Sprite):
def __init__(self, image: str):
self.__image = games.load_image(image)
# Is the car really instantiated at the point the mouse currently is at?
super().__init__(image = self.__image,
x = games.mouse.x,
bottom = games.screen.height - 10)
# Is 'score' really a field of 'car'?
self.score = games.Text(value = 0,
size = 25,
color = color.yellow,
top = 5,
right = games.screen.width/2)
games.screen.add(self.score)
def update(self):
self.x = games.mouse.x
self.left = 65 if self.left < 65 else self.left
# This should probably be defined in a broader scope
max_right = games.screen.width - 65
self.right = self.right if self.right <= max_right else max_right
class Bush(games.Sprite):
speed = 1
def __init__(self,
image: str
x: int,
y: int):
self.__image = games.load_image(image)
self.__start_x = x
self.__start_y = y
super().__init__(image = self.__image,
x = x,
y = y,
dy = Bush.speed)
def update(self):
if self.bottom > games.screen.height/2:
# uses the same initial values as this object
newbush = Bush(self.__image,
self.__start_x,
-100) # start vertically above the screen
games.screen.add(newbush)
#Start
def main():
road = games.load_image("road.jpg", transparent = False)
games.screen.background = road
bush1 = Bush("C:/python/bush.bmp", 20, 100)
bush2 = Bush("C:/python/bush.bmp", 515, 100)
car = Car("C:/python/car.bmp")
games.screen.add(bush1)
games.screen.add(bush2)
games.screen.add(car)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
main()
As the title says. Using batch drawing I get really good performance, even with 4096 sprites. However, since my sprites need to change their underlying image I run into issues with performance. I'm pretty sure I'm doing something silly here, since I specifically created a grid/sprite sheet to handle this effectively. But, of course, I never really use it in any effective manner. I might as well have had 5 different images.
What I really want is to keep the underlying sprite image constant, but shift the visible part based on the "food" metric. Here's the code:
import sys, pyglet, random, time
# Constants.
WIDTH = 1280
HEIGHT = 960
TARGET_FPS = 60
GROWTH_CHANCE = 0.1
fps = 0
screen = pyglet.window.Window(WIDTH, HEIGHT)
random.seed(time.time())
# Here we load universal assets, images, sounds, etc.
grass_tiles_img = pyglet.image.load('grass_tiles.png')
grass_tiles_grid = pyglet.image.ImageGrid(grass_tiles_img, 1, 5)
# Sprite batches.
grass_batch = pyglet.graphics.Batch()
class GrassTile:
'''Define a grass tile which cows can graze on.'''
def __init__(self, x, y, food):
self.food = food
self.sprite = pyglet.sprite.Sprite(grass_tiles_grid[0], x, y,
batch=grass_batch)
def draw(self):
grid_index = (self.food // 20)
self.sprite.image = grass_tiles_grid[grid_index]
return self.sprite
def grow(self):
if random.random() < GROWTH_CHANCE:
self.food = min(self.food + 1, 99)
#screen.event
def on_close():
sys.exit()
#screen.event
def on_draw():
# Clear the screen.
screen.clear()
# Draw grass.
grass_sprites = []
for grass in grass_tiles:
grass_sprites.append(grass.draw())
grass_batch.draw()
# Draw FPS counter.
label = pyglet.text.Label('FPS: ' + str(fps), 'Times New Roman', 12, 10, 10)
label.draw()
def grow_grass(dt):
for grass in grass_tiles:
grass.grow()
def calculate_fps(dt):
global fps
fps = round(min(pyglet.clock.get_fps(), TARGET_FPS))
grass_tiles = [GrassTile(20 * i, 15 * j, 0) for j in range(64) for i in range(64)]
pyglet.clock.schedule_interval(grow_grass, 1 / TARGET_FPS)
pyglet.clock.schedule_interval(calculate_fps, 1 / TARGET_FPS)
pyglet.app.run()
And here's the image so you can run the code:
https://i.imgur.com/kFe91aA.png
Why not just have the image change during grass.grow()?
You don't need to do anything to the grass in the draw phase except draw the batch. Setting the image of a sprite isn't a draw operation, it just changes texture coordinates.
def grow(self):
if random.random() < GROWTH_CHANCE:
self.food = min(self.food + 1, 99)
grid_index = (self.food // 20)
self.sprite.image = grass_tiles_grid[grid_index]
You also shouldn't be recreating the label every draw frame. Create the label beforehand and just update the text. label.text = f'FPS: {fps}'
I've made a slider which should update the size of a grid that I have made. When the slider is clicked and moved the value of the slider changes however the grid size stays the same and does not update. How would I solve this? thanks.
Here is the function that I use to update the slider value. This is called when the slider is clicked in the game loop
def slider_loop(s):
s.move()
grid_size = int(slider_value.val)
return grid_size
here is the section in the main game loop where the slider loop is called
for s in slides:
if s.hit:
slider_loop(s)
here is the slider class
class slider():
def __init__(self, name, val, maxi, mini, x_pos, y_pos):
font = pygame.font.SysFont("Verdana", 12)
self.val = val # start value
self.maxi = maxi # maximum at slider position right
self.mini = mini # minimum at slider position left
self.xpos = x_pos # x-location on screen
self.ypos = y_pos
self.surf = pygame.surface.Surface((100, 50))
self.hit = False # the hit attribute indicates slider movement due to mouse interaction
self.txt_surf = font.render(name, 1, black)
self.txt_rect = self.txt_surf.get_rect(center=(50, 15))
# Static graphics - slider background #
self.surf.fill((100, 100, 100))
pygame.draw.rect(self.surf, grey, [0, 0, 100, 50], 3)
pygame.draw.rect(self.surf, orange, [10, 10, 80, 10], 0)
pygame.draw.rect(self.surf, white, [10, 30, 80, 5], 0)
self.surf.blit(self.txt_surf, self.txt_rect) # this surface never changes
# dynamic graphics - button surface #
self.button_surf = pygame.surface.Surface((20, 20))
self.button_surf.fill(trans)
self.button_surf.set_colorkey(trans)
pygame.draw.circle(self.button_surf, black, (10, 10), 6, 0)
pygame.draw.circle(self.button_surf, orange, (10, 10), 4, 0)
def draw(self):
""" Combination of static and dynamic graphics in a copy of
the basic slide surface
"""
# static
surf = self.surf.copy()
# dynamic
pos = (10+int((self.val-self.mini)/(self.maxi-self.mini)*80), 33)
self.button_rect = self.button_surf.get_rect(center=pos)
surf.blit(self.button_surf, self.button_rect)
self.button_rect.move_ip(self.xpos, self.ypos) # move of button box to correct screen position
# screen
screen.blit(surf, (self.xpos, self.ypos))
def move(self):
"""
The dynamic part; reacts to movement of the slider button.
"""
self.val = (pygame.mouse.get_pos()[0] - self.xpos - 10) / 80 * (self.maxi - self.mini) + self.mini
if self.val < self.mini:
self.val = self.mini
if self.val > self.maxi:
self.val = self.maxi
Looks like grid_size is returned by slider_loop but never use in your code.
Do something like
new_grid_size = slider_loop(s)
if you want to get it.
Your for-loop should probably be like this:
(because currently you're not using the returned grid_size value at all)
for s in slides:
if s.hit:
grid_size = slider_loop(s)
And your function should maybe be corrected to be like this:
def slider_loop(s):
s.move()
grid_size = int(s.val)
return grid_size