Related
I have a question about using pyqt5 and pygame. I have already made a pygame script and a pyqt5 script. The problem is that when I want to make pygame excute the game, it shows a ranking board in pyqt5 and plays game by a pygame script.
This is my pyqt UI code:
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QMovie
import rankingUI
import sys
import main
import pickle
import subprocess
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.setFixedSize(800,400)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
# create label
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.move(0,0)
# start button
self.button = QtWidgets.QPushButton(self.centralwidget)
self.button.setGeometry(320,300,150,100)
self.button.setStyleSheet("border-image:url(./assets/ui/start_btn.png); border:0px;")
self.button.clicked.connect(self.game_start)
# title
self.title = QtWidgets.QLabel(self.centralwidget)
self.title.setGeometry(250, 10, 300, 100)
self.title.setStyleSheet("border-image:url(./assets/ui/dinotitle.png); border:0px;")
# input nick
self.nick_inp = QtWidgets.QLineEdit("ENTER YOUR NICK",self.centralwidget)
self.nick_inp.setAlignment(QtCore.Qt.AlignCenter)
self.nick_inp.setGeometry(320,290, 150 ,20)
#ranking
self.ranking_btn = QtWidgets.QPushButton(self.centralwidget)
self.ranking_btn.setStyleSheet("border-image:url(./assets/ui/rank_btn.png); border:0px;")
self.ranking_btn.setGeometry(730, 325, 50, 50)
self.ranking_btn.clicked.connect(self.popup_ranking)
# add popup
self.add_dia = QtWidgets.QDialog()
self.rank_dia = QtWidgets.QDialog()
# add label to main window
MainWindow.setCentralWidget(self.centralwidget)
# set qmovie as label
self.movie = QMovie("assets/ui/dinogif.gif")
self.label.setMovie(self.movie)
self.movie.start()
def game_start(self):
player_nick = self.nick_inp.text()
if(len(player_nick)==0):
self.nick_inp.setText("ENTER YOUR NICK")
return
main.game_start(player_nick)
def popup_ranking(self):
# ui init
self.rank_dia.setWindowModality(QtCore.Qt.ApplicationModal)
self.rank_dia.setWindowTitle("RANKING")
self.rank_dia.setFixedSize(500,330)
rank_label = QtWidgets.QLabel("RANKKING")
rank_label.setAlignment(QtCore.Qt.AlignCenter)
rank_label.setFont(QtGui.QFont('Arial', 30))
output = QtWidgets.QTextEdit()
output.setFont(QtGui.QFont('Ubuntu',15))
window = QtWidgets.QVBoxLayout()
window.addWidget(rank_label)
window.addWidget(output)
# read data
rank_list = []
ranking_dat = open("ranking.dat", 'rb')
try:
rank_list = pickle.load(ranking_dat)
except:
pass
ranking_dat.close()
# write data
strFormat = '%-18s%-18s%-18s\n'
strOut = strFormat % ('RANK', 'SCORE', 'NICK')
rank_num = 1
strFormat = '%-20s%-20s%-20s\n'
for x in sorted(rank_list, key=lambda s: s["Score"], reverse=True):
tmp = []
tmp.append(str(rank_num))
rank_num += 1
for y in x:
tmp.append(str(x[y]))
strOut += strFormat % (tmp[0], tmp[2], tmp[1])
if rank_num == 10:
break
output.setText(strOut)
self.rank_dia.setLayout(window)
self.rank_dia.show()
# def score_reg(self):
# # popup UI setting
# self.add_dia.setWindowTitle("score registration")
# self.add_dia.setWindowModality(QtCore.Qt.ApplicationModal)
# self.add_dia.setFixedSize(300,70)
#
# # add widget
# nick_label = QtWidgets.QLabel("Insert Nickname :")
# self.nick_input = QtWidgets.QLineEdit()
# score_label = QtWidgets.QLabel("Your Score : ")
# self.score_input = QtWidgets.QLabel("333")
# reg_btn = QtWidgets.QPushButton("register")
# reg_btn.clicked.connect(self.register)
#
# h_box1 = QtWidgets.QHBoxLayout()
# h_box1.addWidget(nick_label)
# h_box1.addWidget(self.nick_input)
#
# h_box2 = QtWidgets.QHBoxLayout()
# h_box2.addWidget(score_label)
# h_box2.addWidget(self.score_input)
# h_box2.addStretch()
# h_box2.addWidget(reg_btn)
#
# v_box = QtWidgets.QVBoxLayout()
# v_box.addLayout(h_box1)
# v_box.addLayout(h_box2)
#
# self.add_dia.setLayout(v_box)
# self.add_dia.show()
#
# def register(self):
# print(self.nick_input.text())
# print(self.score_input.text())
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(window)
window.show()
sys.exit(app.exec_())
This is the pygame code:
import pygame as pyg
import os
import random
import sys
import pickle
import pygame.time
import ui
nickname = ...
# 화면 크기
SCREEN_HEIGHT = 600
SCREEN_WIDTH = 1600
SCREEN = ...
# 달리는 모션 (running1, running2)
RUNNING_MOTIONS = [pyg.image.load(os.path.join("assets/Dino", "DinoRun1.png")),
pyg.image.load(os.path.join("assets/Dino", "DinoRun2.png"))]
# 뛰는 모션, 숙이는 모션 (stooping1, stooping2)
JUMPING_MOTION = pyg.image.load(os.path.join("assets/Dino", "DinoJump.png"))
STOOPING_MOTIONS = [pyg.image.load(os.path.join("assets/Dino", "DinoStoop1.png")),
pyg.image.load(os.path.join("assets/Dino", "DinoStoop2.png"))]
# 선인장
SMALL_CACTUS_IMG = [pyg.image.load(os.path.join("assets/Cactus", "SmallCactus1.png")),
pyg.image.load(os.path.join("assets/Cactus", "SmallCactus2.png")),
pyg.image.load(os.path.join("assets/Cactus", "SmallCactus3.png"))]
LARGE_CACTUS_IMG = [pyg.image.load(os.path.join("assets/Cactus", "LargeCactus1.png")),
pyg.image.load(os.path.join("assets/Cactus", "LargeCactus2.png")),
pyg.image.load(os.path.join("assets/Cactus", "LargeCactus3.png"))]
# 새 모션
BIRD_MOTIONS = [pyg.image.load(os.path.join("assets/Bird", "Bird1.png")),
pyg.image.load(os.path.join("assets/Bird", "Bird2.png"))]
# 기타 (구름, 바닥) -> 하트 추가 예정
CLOUD = pyg.image.load(os.path.join("assets/Other", "Cloud.png"))
GROUND = pyg.image.load(os.path.join("assets/Other", "Track.png"))
global points
class Dinosaur():
X_Dino = 80
Y_Dino = 310
Y_DinoStoop = 340
Jump_height = 8.5
hitScale = 0.5
def __init__(self):
self.stoop_img = STOOPING_MOTIONS
self.run_img = RUNNING_MOTIONS
self.jump_img = JUMPING_MOTION
self.dino_stoop = False
self.dino_run = True
self.dino_jump = False
self.step_index = 0 # 움직임 인덱스
self.jump_height = self.Jump_height
self.image = self.run_img[0] # 0, 1 인덱스 반복하여 애니메이션 구현
self.dino_hitbox = self.image.get_rect() # 공룡 히트박스 설정
self.dino_hitbox.x = self.X_Dino * self.hitScale
self.dino_hitbox.y = self.Y_Dino * self.hitScale
def update(self, Input):
if self.dino_stoop:
self.stoop()
if self.dino_run:
self.run()
if self.dino_jump:
self.jump()
if self.step_index >= 10:
self.step_index = 0
# 공룡 동작
# 점프
if Input[pyg.K_UP] and not self.dino_jump:
self.dino_stoop = False
self.dino_run = False
self.dino_jump = True
# 숙이기
elif Input[pyg.K_DOWN] and not self.dino_jump:
self.dino_stoop = True
self.dino_run = False
self.dino_jump = False
# 달리기
elif not (self.dino_jump or Input[pyg.K_DOWN]):
self.dino_stoop = False
self.dino_run = True
self.dino_jump = False
def stoop(self):
self.image = self.stoop_img[self.step_index // 5]
self.dino_hitbox = self.image.get_rect()
self.dino_hitbox.x = self.X_Dino
self.dino_hitbox.y = self.Y_DinoStoop
self.step_index += 1
def run(self):
self.image = self.run_img[self.step_index // 5] # 5로 해야 속도 맞음
self.dino_hitbox = self.image.get_rect()
self.dino_hitbox.x = self.X_Dino
self.dino_hitbox.y = self.Y_Dino
self.step_index += 1
def jump(self):
self.image = self.jump_img
if self.dino_jump:
self.dino_hitbox.y -= self.jump_height * 4
self.jump_height -= 0.8
if self.jump_height < - self.Jump_height:
self.dino_jump = False
self.jump_height = self.Jump_height
def draw(self, SCREEN):
SCREEN.blit(self.image, (self.dino_hitbox.x, self.dino_hitbox.y))
class Cloud():
def __init__(self):
self.x = SCREEN_WIDTH + random.randint(800, 1000)
self.y = random.randint(50, 100)
self.image = CLOUD
self.width = self.image.get_width()
def update(self):
self.x -= game_speed
if self.x < - self.width:
self.x = SCREEN_WIDTH + random.randint(2600, 3000)
self.y = random.randint(50, 100)
def draw(self, SCREEN):
SCREEN.blit(self.image, (self.x, self.y))
class Obstacle():
def __init__(self, image, type):
self.image = image
self.type = type
self.rect = self.image[self.type].get_rect()
self.rect.x = SCREEN_WIDTH
def update(self):
self.rect.x -= game_speed
if self.rect.x < - self.rect.width:
obstacles.pop()
def draw(self, SCREEN):
SCREEN.blit(self.image[self.type], self.rect)
class SmallCactus(Obstacle):
def __init__(self, image):
self.type = random.randint(0, 2)
super().__init__(image, self.type)
self.rect.y = 325
class LargeCactus(Obstacle):
def __init__(self, image):
self.type = random.randint(0, 2)
super().__init__(image, self.type)
self.rect.y = 300
class Bird(Obstacle):
def __init__(self, image):
self.type = 0
super().__init__(image, self.type)
self.rect.y = 250
self.index = 0
def draw(self, SCREEN):
if self.index >= 9:
self.index = 0
SCREEN.blit(self.image[self.index // 5], self.rect)
self.index += 1
def main():
global game_speed, x_ground, y_ground, points, obstacles
run = True
clock = pyg.time.Clock()
cloud = Cloud()
player = Dinosaur()
game_speed = 14
x_ground = 0
y_ground = 380
points = 0
font = pyg.font.Font('freesansbold.ttf', 20)
obstacles = []
death_cnt = 0
def score():
global points, game_speed
points += 1
if points % 100 == 0:
game_speed += 1
text = font.render("points: " + str(points), True, (0,0,0))
text_rect = text.get_rect()
text_rect.center = (1000, 40)
SCREEN.blit(text, text_rect)
def ground():
global x_ground, y_ground
image_width = GROUND.get_width()
SCREEN.blit(GROUND, (x_ground, y_ground))
SCREEN.blit(GROUND, (image_width + x_ground, y_ground))
if x_ground <= - image_width:
SCREEN.blit(GROUND, (image_width + x_ground, y_ground))
x_ground = 0
x_ground -= game_speed
while run:
for pyEvent in pyg.event.get():
if pyEvent.type == pyg.QUIT:
sys.exit()
SCREEN.fill((255,255,255))
userInput = pyg.key.get_pressed()
player.draw(SCREEN)
player.update(userInput)
if len(obstacles) == 0:
if random.randint(0, 2) == 0:
obstacles.append(SmallCactus(SMALL_CACTUS_IMG))
elif random.randint(0, 2) == 1:
obstacles.append(LargeCactus(LARGE_CACTUS_IMG))
elif random.randint(0, 2) == 2:
obstacles.append(Bird(BIRD_MOTIONS))
for ob in obstacles:
ob.draw(SCREEN)
ob.update()
if player.dino_hitbox.colliderect(ob.rect):
pyg.time.delay(500)
death_cnt += 1
menu(death_cnt)
ground()
cloud.draw(SCREEN)
cloud.update()
score()
clock.tick(30)
pyg.display.update()
def menu(death_cnt):
global points
run = True
if death_cnt == 0:
points = 0
while run:
update(points)
SCREEN.fill((255,255,255))
font = pyg.font.Font('freesansbold.ttf', 30) # 폰트 적용 오류......
if death_cnt == 0:
text = font.render("Press any key to Start", True, (0,0,0)) # 한글 "시작하기" 로 변경 예정
text1 = font.render("DinoSaurGame", True, (0,0,0)) # "공룡게임"으로 변경 예정
elif death_cnt > 0:
text = font.render("Press any key to Restart", True, (0,0,0))
score = font.render("Your Score : " + str(points), True, (0,0,0))
scoreRect = score.get_rect()
scoreRect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 50)
SCREEN.blit(score, scoreRect)
textRect = text.get_rect()
textRect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
SCREEN.blit(text, textRect)
SCREEN.blit(RUNNING_MOTIONS[0], (SCREEN_WIDTH // 2 - 20, SCREEN_HEIGHT // 2 - 140))
pyg.display.update()
for pyEvent in pyg.event.get():
if pyEvent.type == pyg.QUIT:
sys.exit()
if pyEvent.type == pyg.KEYDOWN:
main()
def update(score):
pass
def game_start(nick):
pyg.init()
global SCREEN
global nickname
nickname = nick
SCREEN = pyg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
menu(death_cnt=0)
When I quit the pygame window, pyqt5 quits too. How can I only quit the pygame window?
Do not mix frameworks, mixing frameworks always means some kind of undefined behavior. The frameworks may interact poorly or completely conflict with one another. Getting it to work on your system doesn't mean it will work on another system or with a different version of any of the frameworks.
If you use Qt, then I suggest to develop the game with Qt as well (see Qt Based Games).
In ursina, How can I save the render frames as a sequence of images or a video? Ursina cheat sheet has nothing on this.
You can see VideoRecorder :
from ursina import *
import os, shutil
import numpy as np
# import imageio # gets imported in convert_to_gif
# from panda3d.core import PNMImage
class VideoRecorder(Entity):
def __init__(self, duration=5, name='untitled_video', **kwargs):
super().__init__()
self.recording = False
self.file_path = Path(application.asset_folder) / 'video_temp'
self.i = 0
self.duration = duration
self.fps = 30
self.video_name = name
self.t = 0
for key, value in kwargs.items():
setattr(self, key, value)
self.max_frames = int(self.duration * self.fps)
self.frames = []
def start_recording(self):
print('start recording,', self.duration, self.file_path)
window.fps_counter.enabled = False
window.exit_button.visible = False
self.frames = []
self.max_frames = self.duration * self.fps
if not self.file_path.exists():
self.file_path.mkdir()
base.movie(namePrefix=f'\\video_temp\\{self.video_name}', duration=2.0, fps=30, format='png', sd=4)
self.recording = True
invoke(self.stop_recording, delay=self.duration)
def stop_recording(self):
self.recording = False
window.fps_counter.enabled = True
window.exit_button.visible = True
print('stop recording')
self.convert_to_gif()
def update(self):
if not self.recording:
return
self.t += time.dt
if self.t >= 1/30:
base.screenshot(
namePrefix = '\\video_temp\\' + self.video_name + '_' + str(self.i).zfill(4) + '.png',
defaultFilename = 0,
)
self.t = 0
# # self.frames.append(self.renderToPNM())
# image = base.win.getScreenshot()
# data = image.getRamImageAs("RGB").getData()
# # from PIL import Image
# # image = Image.fromarray(data)
# # img = data.convert("RGBA")
# data = np.array(data)
#
# # image = deepcopy(camera.render_texture)
# self.frames.append(data)
self.i += 1
# store screenshot in memory
# def renderToPNM(self):
# base.graphicsEngine.renderFrame()
# if hasattr(camera, 'render_texure'):
# return copy(camera.render_texure)
# # image = PNMImage()
# # dr = base.camNode.getDisplayRegion(0)
# # dr.getScreenshot(image)
# # win.setupRenderTexture()
# return None
def convert_to_gif(self):
import imageio
images = []
if not os.path.exists(self.file_path):
return
for filename in os.listdir(self.file_path):
images.append(imageio.imread(self.file_path/filename))
imageio.mimsave(Path(f'{self.file_path.parent}/{self.video_name}.gif'), images)
shutil.rmtree(self.file_path) # delete temp folder
print('saved gif to:', Path(f'{self.file_path.parent}/{self.video_name}.gif'))
class VideoRecorderUI(WindowPanel):
def __init__(self, **kwargs):
self.duration_label = Text('duration:')
self.duration_field = InputField(default_value='5')
self.fps_label = Text('fps:')
self.fps_field = InputField(default_value='30')
self.name_label = Text('name:')
self.name_field = InputField(default_value='untitled_video')
self.start_button = Button(text='Start Recording [Shift+F12]', color=color.azure, on_click=self.start_recording)
super().__init__(
title='Video Recorder [F12]',
content=(
self.duration_label,
self.duration_field,
self.fps_label,
self.fps_field,
self.name_label,
self.name_field,
Space(1),
self.start_button,
),
)
self.y = .5
self.scale *= .75
self.visible = False
def input(self, key):
if key == 'f12':
self.visible = not self.visible
if held_keys['shift'] and key == 'f12':
self.start_button.on_click()
def start_recording(self):
print(self.name_field)
if self.name_field.text == '':
self.name_field.blink(color.color(0,1,1,.5), .5)
print('enter name')
return
# self.start_button.color=color.lime
self.visible = False
application.video_recorder.duration = float(self.duration_field.text)
application.video_recorder.video_name = self.name_field.text
application.video_recorder.frame_skip = 60 // int(self.fps_field.text)
application.video_recorder.recording = True
if __name__ == '__main__':
app = Ursina()
# window.size = (1600/3,900/3)
# cube = primitives.RedCube()
# cube.animate_x(5, duration=5, curve=curve.linear)
# cube.animate_x(0, duration=5, curve=curve.linear, delay=5)
# vr = VideoRecorder()
# invoke(setattr, vr, 'recording', True, delay=1)
# invoke(os._exit, 0, delay=6)
# vr.recording = True
window.size *= .5
from ursina.prefabs.first_person_controller import FirstPersonController
from ursina.shaders import lit_with_shadows_shader
random.seed(0)
Entity.default_shader = lit_with_shadows_shader
ground = Entity(model='plane', collider='box', scale=64, texture='grass', texture_scale=(4,4))
editor_camera = EditorCamera(enabled=False, ignore_paused=True)
player = FirstPersonController(model='cube', z=-10, color=color.orange, origin_y=-.5, speed=8)
player.collider = BoxCollider(player, Vec3(0,1,0), Vec3(1,2,1))
gun = Entity(model='cube', parent=camera, position=(.5,-.25,.25), scale=(.3,.2,1), origin_z=-.5, color=color.red, on_cooldown=False)
shootables_parent = Entity()
mouse.traverse_target = shootables_parent
for i in range(16):
Entity(model='cube', origin_y=-.5, scale=2, texture='brick', texture_scale=(1,2),
x=random.uniform(-8,8),
z=random.uniform(-8,8) + 8,
collider='box',
scale_y = random.uniform(2,3),
color=color.hsv(0, 0, random.uniform(.9, 1))
)
sun = DirectionalLight()
sun.look_at(Vec3(1,-1,-1))
Sky()
vr = VideoRecorder(duration=10)
def input(key):
if key == '5':
vr.start_recording()
if key == '6':
vr.stop_recording()
app.run()
This code will convert the recording to mp4 video:
from ursina import *
import os, shutil
import numpy as np
# import imageio # gets imported in convert_to_gif
# from panda3d.core import PNMImage
class VideoRecorder(Entity):
def __init__(self, duration=5, name='untitled_video', **kwargs):
super().__init__()
self.recording = False
self.file_path = Path(application.asset_folder) / 'video_temp'
self.i = 0
self.duration = duration
self.fps = 30
self.video_name = name
self.t = 0
for key, value in kwargs.items():
setattr(self, key, value)
self.max_frames = int(self.duration * self.fps)
self.frames = []
def start_recording(self):
print('start recording,', self.duration, self.file_path)
window.fps_counter.enabled = False
window.exit_button.visible = False
self.frames = []
self.max_frames = self.duration * self.fps
if not self.file_path.exists():
self.file_path.mkdir()
base.movie(namePrefix=f'\\video_temp\\{self.video_name}', duration=2.0, fps=30, format='png', sd=4)
self.recording = True
invoke(self.stop_recording, delay=self.duration)
def stop_recording(self):
self.recording = False
window.fps_counter.enabled = True
window.exit_button.visible = True
print('stop recording')
# self.convert_to_gif()
self.convert_to_vid()
def update(self):
if not self.recording:
return
self.t += time.dt
if self.t >= 1/30:
base.screenshot(
namePrefix = '\\video_temp\\' + self.video_name + '_' + str(self.i).zfill(4) + '.png',
defaultFilename = 0,
)
self.t = 0
self.i += 1
def convert_to_gif(self):
import imageio
images = []
if not os.path.exists(self.file_path):
return
for filename in os.listdir(self.file_path):
images.append(imageio.imread(self.file_path/filename))
imageio.mimsave(Path(f'{self.file_path.parent}/{self.video_name}.gif'), images)
shutil.rmtree(self.file_path) # delete temp folder
print('saved gif to:', Path(f'{self.file_path.parent}/{self.video_name}.gif'))
def convert_to_vid(self):
import imageio
images = []
if not os.path.exists(self.file_path):
return
writer = imageio.get_writer('test.mp4', fps=self.fps)
for file in os.listdir(self.file_path):
im = imageio.imread(self.file_path/file)
writer.append_data(im)
writer.close()
print('Video saved!!')
class VideoRecorderUI(WindowPanel):
def __init__(self, **kwargs):
self.duration_label = Text('duration:')
self.duration_field = InputField(default_value='5')
self.fps_label = Text('fps:')
self.fps_field = InputField(default_value='30')
self.name_label = Text('name:')
self.name_field = InputField(default_value='untitled_video')
self.start_button = Button(text='Start Recording [Shift+F12]', color=color.azure, on_click=self.start_recording)
super().__init__(
title='Video Recorder [F12]',
content=(
self.duration_label,
self.duration_field,
self.fps_label,
self.fps_field,
self.name_label,
self.name_field,
Space(1),
self.start_button,
),
)
self.y = .5
self.scale *= .75
self.visible = False
def input(self, key):
if key == 'f12':
self.visible = not self.visible
if held_keys['shift'] and key == 'f12':
self.start_button.on_click()
def start_recording(self):
print(self.name_field)
if self.name_field.text == '':
self.name_field.blink(color.color(0,1,1,.5), .5)
print('enter name')
return
# self.start_button.color=color.lime
self.visible = False
application.video_recorder.duration = float(self.duration_field.text)
application.video_recorder.video_name = self.name_field.text
application.video_recorder.frame_skip = 60 // int(self.fps_field.text)
application.video_recorder.recording = True
if __name__ == '__main__':
app = Ursina()
# window.size = (1600/3,900/3)
# cube = primitives.RedCube()
# cube.animate_x(5, duration=5, curve=curve.linear)
# cube.animate_x(0, duration=5, curve=curve.linear, delay=5)
# vr = VideoRecorder()
# invoke(setattr, vr, 'recording', True, delay=1)
# invoke(os._exit, 0, delay=6)
# vr.recording = True
window.size *= .5
from ursina.prefabs.first_person_controller import FirstPersonController
from ursina.shaders import lit_with_shadows_shader
random.seed(0)
Entity.default_shader = lit_with_shadows_shader
ground = Entity(model='plane', collider='box', scale=64, texture='grass', texture_scale=(4,4))
editor_camera = EditorCamera(enabled=False, ignore_paused=True)
player = FirstPersonController(model='cube', z=-10, color=color.orange, origin_y=-.5, speed=8)
player.collider = BoxCollider(player, Vec3(0,1,0), Vec3(1,2,1))
gun = Entity(model='cube', parent=camera, position=(.5,-.25,.25), scale=(.3,.2,1), origin_z=-.5, color=color.red, on_cooldown=False)
shootables_parent = Entity()
mouse.traverse_target = shootables_parent
for i in range(16):
Entity(model='cube', origin_y=-.5, scale=2, texture='brick', texture_scale=(1,2),
x=random.uniform(-8,8),
z=random.uniform(-8,8) + 8,
collider='box',
scale_y = random.uniform(2,3),
color=color.hsv(0, 0, random.uniform(.9, 1))
)
sun = DirectionalLight()
sun.look_at(Vec3(1,-1,-1))
Sky()
vr = VideoRecorder(duration=10)
def input(key):
if key == '5':
vr.start_recording()
if key == '6':
vr.stop_recording()
app.run()
I have a assignment that ask me to make a snake game with Python. The assignment asks me to make the snake's body gets trimmed when its head touches the body.
The snake body is a queue, so here is the code for forming a queue:
The snake's head is defined as the rear node and the snake's tail is defined as the front node, as shown in the picture below.
#The code of forming a queue, it cannot be modified
class Node:
def __init__(self, x, y):
self.x = x
self.y = y
self.pre = None
self.next = None
class Queue:
def __init__(self):
self.front = None
self.rear = None
def len(self):
length = 0
cur = self.front
while cur:
cur = cur.next
length += 1
return length
def enQueue(self, x, y):
new = Node(x, y)
if self.len() == 0:
self.front = new
self.rear = new
else:
new.pre = self.rear
self.rear.next = new
self.rear = new
def deQueue(self):
if self.len() <= 1:
self.front = None
self.rear = None
else:
self.front = self.front.next
self.front.pre = None
def reverse(self):
cur = self.front
self.rear, self.front = self.front , self.rear
while cur:
cur.next, cur.pre = cur.pre, cur.next
cur = cur.pre
def printQueue(self):
cur = self.front
print("front", end=" ")
while cur:
print(f"[{cur.x}, {cur.y}]", end=" ")
cur = cur.next
print("rear")
And this is the main code for the game:
import pygame, sys, time, random
from pygame.locals import *
from pathlib import Path, os
from coor_queue import Node, Queue
from item_stack import Stack
class SnakeGame:
def __init__(self):
self.g = 30 #The width of each grid
self.dir = "right" #Initial Direction
self.snake = Queue() #Queue of the snake
for i in range(9):
self.snake.enQueue(i*self.g,9*self.g)
self.init_params()
self.init_pygame()
self.init_objects()
self.init_images()
#The code above cannot be modified
# =========================== Movement ===========================
def move(self):
headx = self.snake.rear.x
heady = self.snake.rear.y
if self.dir == "down":
heady += self.g
self.snake.enQueue(headx, heady)
self.snake.deQueue()
if self.dir == "up":
heady -= self.g
self.snake.enQueue(headx, heady)
self.snake.deQueue()
if self.dir == "left":
headx -= self.g
self.snake.enQueue(headx, heady)
self.snake.deQueue()
if self.dir == "right":
headx += self.g
self.snake.enQueue(headx, heady)
self.snake.deQueue()
# =========================== Add Tails ===========================
def add_tail(self):
tailx = self.snake.front.x
taily = self.snake.front.y
newtail = Node(tailx, taily)
newtail.next = self.snake.front
self.snake.front.pre = newtail
self.snake.front = newtail
# =========================== Trim the snake's body ===========================
def eat_body(self):
# If the snake's head touches a grid (node) of the body
# Then the part from original tail to the grid which the snake's head touches
# Hint: Use self.snake
# ============The code below cannot be modified======================
def main(self):
while True:
self.keyboard_input()
self.check_input_valid()
self.move()
self.eat()
self.display()
if self.is_dead():
self.game_over()
self.fps.tick(8 + self.speed//5)
def keyboard_input(self): # Keyboard input
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == ord("d") or event.key == K_RIGHT: self.input_dir = "right"
if event.key == ord("a") or event.key == K_LEFT: self.input_dir = "left"
if event.key == ord("w") or event.key == K_UP: self.input_dir = "up"
if event.key == ord("s") or event.key == K_DOWN: self.input_dir = "down"
if event.key == K_ESCAPE: pygame.event.post(pygame.event.Event(QUIT))
def check_input_valid(self): #If the input direction is the opposite of the original direction, the input will be invalid
if (self.input_dir == "right" and self.dir != "left") or (self.input_dir == "left" and self.dir != "right") or \
(self.input_dir == "up" and self.dir != "down") or (self.input_dir == "down" and self.dir != "up"):
self.dir = self.input_dir
def eat(self): #Eat body or food
self.eat_food()
self.eat_body()
def display(self):
#Background
self.blit_map()
#Snake body
cur = self.snake.rear.pre
while cur:
self.blit_image("body", cur.x, cur.y, self.g, self.g)
cur = cur.pre
#Snake head
if self.snake.rear.x < self.width:
self.blit_image("head", self.snake.rear.x, self.snake.rear.y, self.g, self.g)
#Food
self.blit_image(self.food, self.foodPos.x, self.foodPos.y, self.g, self.g)
#Status Bar
self.blit_status_bar()
pygame.display.flip()
def is_dead(self):
return (self.snake.rear.x == self.width or self.snake.rear.x < 0) or \
(self.snake.rear.y == self.height or self.snake.rear.y < 0)
def game_over(self): # Game Over
self.play_theme("game_over1")
time.sleep(3)
pygame.quit()
sys.exit()
def init_params(self): #Basic parameters
self.screen_width = 1140
self.screen_height = 540
self.width = 960
self.height = 540
self.speed = 0
self.prob = 25
self.score = 0
self.spf = 10
self.satiety = 0
self.input_dir = None
def init_pygame(self): #Initialize pygame
pygame.init()
self.fps = pygame.time.Clock() #
self.screen = pygame.display.set_mode((self.screen_width, self.screen_height)) #
pygame.display.set_caption("Snake Game") #
pygame.mixer.init()
def init_objects(self):
self.food_list = [food.split(".")[0] for food in os.listdir(Path("src/image/food"))]
self.food = None
self.select_food()
self.foodPos = Node(self.width//2, self.height//2)
def init_images(self):
self.img_StatusBar = pygame.image.load(Path("src/image/other/StatusBar.jpg")).convert_alpha()
self.img_head = pygame.image.load(Path("src/image/snake/SnakeHead.jpg")).convert_alpha()
self.img_body = pygame.image.load(Path("src/image/snake/SnakeBody.jpg")).convert_alpha()
self.img_map = pygame.image.load(Path("src/image/map/Map1.jpg")).convert_alpha()
for food in self.food_list:
exec(f"self.img_{food} = pygame.image.load(Path('src/image/food/{food}.jpg')).convert_alpha()")
def eat_food(self):
if self.snake.rear.x == self.foodPos.x and self.snake.rear.y == self.foodPos.y:
self.satiety += 1
self.speed += 1
self.spf = 10 + ((self.satiety-1)//10)*10
self.score += self.spf
self.play_effect("eat_food")
x = random.randrange(1, self.width//self.g)
y = random.randrange(1, self.height//self.g)
self.foodPos.x = x*self.g
self.foodPos.y = y*self.g
self.select_food()
self.add_tail()
def select_food(self):
while True:
next_food = self.food_list[random.randrange(len(self.food_list))]
if next_food != self.food:
self.food = next_food
break
def play_theme(self, theme):
pygame.mixer.music.load(Path(f"src/sound/theme/{theme}.mp3"))
pygame.mixer.music.set_volume(0.5)
pygame.mixer.music.play()
def play_effect(self, effect):
sound = pygame.mixer.Sound(Path(f"src/sound/effect/{effect}.mp3"))
sound.set_volume(0.7)
sound.play()
def blit_image(self, name, coor_x = 0, coor_y = 0, size_x = 0, size_y = 0):
exec(f"self.img_{name} = pygame.transform.scale(self.img_{name}, size=(size_x, size_y))")
exec(f"self.screen.blit(self.img_{name}, (coor_x, coor_y))")
def blit_rect(self, data, coor_x = 0, coor_y = 0, font_size = 80, color = (0, 0, 0)):
self.Font = pygame.font.SysFont("", font_size)
self.Surf = self.Font.render(str(data), True, color)
self.Rect = self.Surf.get_rect()
self.Rect.center = (coor_x, coor_y)
self.screen.blit(self.Surf, self.Rect)
def blit_map(self):
self.blit_image("map", 0, 0, self.width, self.height)
def blit_status_bar(self):
self.blit_image("StatusBar", 960, 0, 180, self.screen_height)
self.blit_rect(self.score, 35*self.g, 1.87*self.g, font_size = 50, color = (238, 0, 0))
self.blit_rect(int(8 + self.speed)-7, 35*self.g, 4.13*self.g, font_size = 50, color = (178, 58, 238))
self.blit_rect(self.snake.len(), 35*self.g, 6.28*self.g, font_size = 50, color = (50, 205, 50))
# ==================================================================
game = SnakeGame()
game.main()
The picture below explains how trimming snake body works, the Python logo is the head.
I understand that the condition of trimming the body is when the head reaches the same grid as a part of the body. But I have no idea how to write this condition because I'm not sure if there is a way to get the coordinate from the nodes between front node and rear node.
Some further explanation is appreciated, thanks in advance.
Use a loop to compare the head of the queue against all elements of the body. The loop is similar to the loop in the print method. Return true if the position of the head is equal to a body position, else return false:
def isHeadEqualAnyBodyItem(snake):
head = snake.front
if not head:
return False
body = head.next
while body:
if head.x == body.x and head.y == body.y
return True
body = body.next
return False
I have some questions about this game I'm making...
First, When I press F which makes the tank fire, the weapon_explosion.zip's images are all placed on top of each other and causes massive lag.
Second, After I fire lots of buggy things happen:
Firing again makes the bullet go the same direction again.
The bullet accelerates to extremely high speeds ?
(The explosion lags)
Third:
How do I make a .zip animation go once, I just went around it by destroying it after ~2.5 seconds
Now, here is my code:
PlayerImage Class (The tank):
class PlayerImage(Image):
angle = NumericProperty(0)
def __init__(self, **kwargs):
self.tuplePos = None
self.y = 122
self.eventf = None
self.eventr = None
self.source = './rpgArt/tankright.zip'
Clock.schedule_interval(lambda dt: self.gravity(), 1.0 / 60.0)
super(PlayerImage, self).__init__(**kwargs)
self._keyboard = Window.request_keyboard(self, None)
if not self._keyboard:
return
self._keyboard.bind(on_key_down=self.on_key_down)
self.gravVelocity = 1
self.bullet = ""
self.explosion = None
self.iterationBul = 0
self.left = None
self.iterations = 0
self.weapon = "./rpgArt/"
self.redtank = "./rpgArt/tankModels/tank_red/"
self.bluetank = "./rpgArt/tankModels/tank_red/"
self.redtankRight = self.redtank + "normal/tank_red_right.zip"
self.redtankLeft = self.redtank + "normal/tank_red_left.zip"
self.bullet = Image(source=self.weapon + "weapon_bullet.zip")
def on_key_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == "right" or keycode[1] == "d":
self.source = self.redtankRight
self.x += 5
if keycode[1] == "left" or keycode[1] == "a":
self.source = self.redtankLeft
self.x -= 5
if keycode[1] == "up" or keycode[1] == "spacebar" or keycode[1] == "w":
if self.source == self.redtankLeft:
self.source = self.redtankLeft
if self.source == self.redtankRight:
self.source = self.redtankRight
self.y += 50
if keycode[1] == "f":
if self.source == self.redtankLeft:
self.bullet = Image(source=self.weapon + "weapon_bullet.zip")
self.source = self.redtank + "fire/tank_red_left_fire.zip"
self.tuplePos = self.x - 50, self.y - 3
self.bullet.pos = self.tuplePos # TODO POSITION BULLET
self.add_widget(self.bullet)
if self.bullet is not None:
self.eventr = Clock.schedule_interval(lambda dt: self.moveBullet(), 1.0 / 60.0) # TODO MOVE BULLET CLOCK
elif self.source == self.redtankRight:
self.bullet = Image(source=self.weapon + "weapon_bullet.zip")
self.source = self.redtank + "fire/tank_red_right_fire.zip"
self.tuplePos = self.x + 50, self.y - 3
self.bullet.pos = self.tuplePos
self.add_widget(self.bullet)
if self.bullet is not None:
self.eventr = Clock.schedule_interval(lambda dt: self.moveBullet(), 1.0 / 60.0)
def moveBullet(self):
if self.iterationBul == 0:
if self.source == self.redtank + "fire/tank_red_left_fire.zip":
self.left = True
self.iterationBul = 1
elif self.source == self.redtank + "fire/tank_red_right_fire.zip":
self.left = False
self.iterationBul = 1
else:
if self.left is True:
self.eventf = Clock.schedule_interval(lambda dt: self.movebulletleft(), 5.0 / 20.0)
if self.left is False:
self.eventf = Clock.schedule_interval(lambda dt: self.movebulletright(), 5.0 / 20.0)
def movebulletleft(self):
if self.bullet is None:
self.bullet = Image(source=self.weapon + "weapon_bullet.zip")
else:
x, y = self.tuplePos
if x < 0:
self.explosion = Image(source=self.weapon + "weapon_explosion.zip")
self.explosion.pos = x, y
self.add_widget(self.explosion)
self.remove_widget(self.bullet)
Clock.unschedule(self.eventf)
Clock.unschedule(self.eventr)
self.iterationBul = 0
self.left = None
self.bullet = None
Clock.schedule_once(lambda dt: self.REexplosion(), 5)
else:
self.bullet.pos = (x - 3, y)
self.tuplePos = self.bullet.pos
print('left is true')
print(str(self.bullet.pos) + " bullet pos")
def REexplosion(self):
self.remove_widget(self.explosion)
def movebulletright(self):
if self.bullet is None:
self.bullet = Image(source=self.weapon + "weapon_bullet.zip")
else:
self.bullet.pos = (x, y) = self.tuplePos
if x > 680:
self.explosion = Image(source=self.weapon + "weapon_explosion.zip")
self.explosion.pos = x, y
self.add_widget(self.explosion)
self.remove_widget(self.bullet)
Clock.unschedule(self.eventf)
Clock.unschedule(self.eventr)
self.iterationBul = 0
self.left = None
self.bullet = None
Clock.schedule_once(lambda dt: self.REexplosion(), 1.7)
else:
v = 3
self.bullet.pos = (x + v, y)
self.tuplePos = self.bullet.pos
print('right is true')
try:
print(str(self.bullet.pos) + " bullet pos")
except AttributeError:
print("bullet out of range!")
def gravity(self):
if self.y == 123:
pass
else:
self.y -= 2
if self.y <= 122:
self.y += 1
# if self.iterations / 60 != 0:
# result = self.iterations / 60
# self.gravVelocity += result
# print(self.y)
def on_touch_down(self, touch):
# self.currentstate = self.states["person.zip/"]
Animation.cancel_all(self)
angle = degrees(atan2(touch.y - self.center_y,
touch.x - self.center_x))
Animation(center=touch.pos, angle=angle).start(self)
# self.currentstate = self.states["personred/rest.png/"]
print(self.y)
Also, my Builder.load_strings:
root = Builder.load_string('''
Widget:
Widget:
canvas:
Color:
rgb: 0,0,1
Rectangle:
size: (900,500)
pos: (0,150)
Widget:
Image:
pos: (0,150)
size: (400,400)
source: './rpgArt/city.zip'
Widget:
canvas:
Color:
rgb: 0,1,0
Rectangle:
size: (900,150)
pos: (0,0)
PlayerImage:
source: './rpgArt/tankModels/tank_red/normal/tank_red_right.zip'
allow_stretch: True
keep_ratio: True
pos:(0,130)
''')
runTouchApp(root)
And the other one which I don't fully understand... (From someone else.)
Builder.load_string('''
<PlayerImage>:
canvas.before:
PushMatrix
canvas.after:
PopMatrix
''')
I needed to unschedule self.eventr at movebullet(self):.
Clock.unschedule(self.eventr)
Below is a little Kivy app I'm working on. At the moment I'm trying to implement a basic reset feature. Essentially, if an enemy collides with the player, I want the game to reset. The games ends by unscheduling clock schedules upon collision with an enemy. I thought after that I could just call main() afterwards, but this simply causes massive bugs where the game spews out errors left and right. I also tried added reschedule rules after the unshedule, but that just overrides the unshedule and keeps the game going.
Note: The game is handled mostly from the game class towards the bottom of the code. It's also where you'll see clock schedule rules.
import kivy
kivy.require('1.1.1')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty, ListProperty, NumericProperty, BooleanProperty
from kivy.core.audio import SoundLoader
import math
from kivy.clock import Clock
import time
import random
sound = SoundLoader.load('gamemusic.mp3')
PLAYER_SPEED = 10
ENEMY_SPEED = 4
ENEMY_SPAWN = 5
UPDATE_SPEED = .01
MIN_INITIAL_PLAYER_MINION_DISTANCE = 200
UPDATE_SCORE = 1
MAX_DECOYS = 3
if sound:
sound.loop = True
sound.play()
class Movable(Widget):
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
target = ListProperty([])
def move(self):
dx = self.target[0] - self.center[0]
dy = self.target[1] - self.center[1]
total = math.sqrt(dx**2 + dy**2)
if total >0:
self.velocity_x = (dx / total) * self.speed
self.velocity_y = (dy / total) * self.speed
new_x = self.pos[0] + self.velocity_x
new_y = self.pos[1] + self.velocity_y
self.pos = (new_x,new_y)
elif total <=0:
self.velocity_x = self.speed
self.velocity_y = self.speed
class Player(Movable):
target = ListProperty([])
def __init__(self, **kwargs):
Widget.__init__(self)
self.target = [399.0, 399.0]
self.speed = PLAYER_SPEED
def update_target(self, new_target):
self.target = new_target
if distance_pos(self.center, self.target) <= self.speed*2:
self.target = self.center
class Enemy(Movable):
randomColor = random.randint(0,1)
alert = BooleanProperty(False)
def __init__(self, **kwargs):
Widget.__init__(self)
self.world_width = kwargs.get("width",0)
self.world_height = kwargs.get("height",0)
self.speed = ENEMY_SPEED
x_target = float(random.randint(0, self.world_width))
y_target = float(random.randint(0, self.world_height))
self.target = [x_target, y_target]
self.center = kwargs.get('start_pos',[0,0])
def update_target(self, player, enemies, decoys):
alert_list = self.alerted(enemies)
if len(decoys) > 0:
self.target = decoys[0].center
self.alert = False
elif distance(player, self) < 150:
self.alert = True
self.target = player.center
elif len(alert_list) > 0:
self.target = alert_list[0].center
self.alert = False
else:
if distance_pos(self.center, self.target) <= self.speed*2:
x_new_target = float(random.randint(0, self.world_width))
y_new_target = float(random.randint(0, self.world_height))
self.target = [x_new_target, y_new_target]
def alerted(self, enemies):
alert_list = []
for item in enemies:
if item.alert == True and distance(self, item) < 150:
alert_list.append(item)
return alert_list
class Decoy(Widget):
def __init__(self, **kwargs):
Widget.__init__(self)
self.center = kwargs.get('start_pos',[0,0])
pass
def distance(widget1, widget2):
return distance_pos(widget1.center, widget2.center)
def distance_pos(pos1, pos2):
dist = math.sqrt((pos1[0]-pos2[0])**2 + (pos1[1]-pos2[1])**2)
return dist
class Game(Widget):
player1 = ObjectProperty(None)
enemies = ListProperty([])
decoys = ListProperty([])
score = NumericProperty()
def setup(self):
self.enemies = []
self.decoys = []
self.player1.center = self.center
self.setup_schedules()
#Don't forget about good code organization!
def setup_schedules(self):
Clock.schedule_interval(self.update, UPDATE_SPEED)
Clock.schedule_interval(self.spawn_enemy, ENEMY_SPAWN)
Clock.schedule_interval(self.increase_score, UPDATE_SCORE)
def update(self,dt):
self.player1.move()
for item in self.enemies:
item.update_target(self.player1,self.enemies,self.decoys)
item.move()
if self.player1.collide_widget(item):
Clock.unschedule(self.spawn_enemy)
Clock.unschedule(self.update)
Clock.unschedule(self.increase_score)
"""
def death_restart(self, player):
if self.collide_widget(player):
print("error")
#main()
"""
def spawn_enemy(self, dt):
if len(self.enemies) <= 8:
x = float(random.randint(0, self.width))
y = float(random.randint(0, self.height))
enemy = Enemy(start_pos = (x,y),width = self.width,height = self.height)
while distance(enemy, self.player1)< MIN_INITIAL_PLAYER_MINION_DISTANCE:
x = float(random.randint(0, self.width))
y = float(random.randint(0, self.height))
enemy.pos = (x,y)
self.enemies.append(enemy)
self.add_widget(enemy)
def spawn_decoy(self, location):
x = location[0]
y = location[1]
decoy = Decoy(start_pos = (x,y))
self.decoys.append(decoy)
self.add_widget(decoy)
def increase_score(self, dt):
self.score += 1
#on_touch_move vs on_touch_down
def on_touch_move(self, touch):
self.player1.update_target([touch.x, touch.y])
def on_touch_down(self, touch):
if touch.is_double_tap and len(self.decoys) < MAX_DECOYS:
self.spawn_decoy([touch.x, touch.y])
#Entry Point into app
class GameApp(App):
def build(self):
game = Game()
game.setup()
return game
def main():
GameApp().run()
main()