Merge branch 'feature/make_food_move' into develop
Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 3.1 MiB |
Before Width: | Height: | Size: 317 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 225 KiB |
After Width: | Height: | Size: 395 KiB |
After Width: | Height: | Size: 415 KiB |
After Width: | Height: | Size: 223 KiB |
After Width: | Height: | Size: 191 KiB |
After Width: | Height: | Size: 346 KiB |
After Width: | Height: | Size: 169 KiB |
After Width: | Height: | Size: 298 KiB |
After Width: | Height: | Size: 299 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 82 KiB |
22
blockly.json
|
@ -9,24 +9,22 @@
|
|||
"SCENE_INFO": [
|
||||
["scene_info['frame']", "# frame", "# 幀數"],
|
||||
["scene_info['status']", "game status", "遊戲狀態"],
|
||||
["scene_info['player_x']", "x coordinate of player", "玩家角色的 x 座標"],
|
||||
["scene_info['player_y']", "y coordinate of player", "玩家角色的 y 座標"],
|
||||
["scene_info['player_size']", "y coordinate of player", "玩家角色的大小"],
|
||||
["scene_info['player_vel']", "velocity of player", "玩家角色的速度"],
|
||||
["scene_info['squid_x']", "x coordinate of squid", "玩家角色的中心 x 座標"],
|
||||
["scene_info['squid_y']", "y coordinate of squid", "玩家角色的中心 y 座標"],
|
||||
["scene_info['squid_w']", "width of squid", "玩家角色的寬度"],
|
||||
["scene_info['squid_h']", "height of squid", "玩家角色的高度"],
|
||||
["scene_info['squid_lv']", "level of squid", "玩家角色的等級"],
|
||||
["scene_info['squid_vel']", "velocity of squid", "玩家角色的速度"],
|
||||
["scene_info['score']", "score", "目前的分數"],
|
||||
["scene_info['foods']", "list of foods positions", "點點的位置清單"],
|
||||
["scene_info['score_to_pass']", "the score for next level", "通關分數"],
|
||||
["scene_info", "dictionary of all information", "包含所有資訊的字典"]
|
||||
],
|
||||
"CONSTANT": [
|
||||
[0, "left boundary", "左邊界"],
|
||||
[800, "right boundary", "右邊界"],
|
||||
[0, "top boundary", "上邊界"],
|
||||
[600, "bottom boundary", "下邊界"],
|
||||
[30, "player size", "玩家角色的初始大小"],
|
||||
[8, "food lv1 size", "等級一食物的寬高度"],
|
||||
[12, "food lv2 size", "等級二食物的高度"],
|
||||
[16, "food lv3 size", "等級三食物的高度"]
|
||||
|
||||
[30, "food lv1 size", "等級一食物的寬高度"],
|
||||
[40, "food lv2 size", "等級二食物的高度"],
|
||||
[50, "food lv3 size", "等級三食物的高度"]
|
||||
],
|
||||
"ACTION": [
|
||||
["['UP']", "moving up", "向上移動"],
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"game_name": "easy_game",
|
||||
"version": "2.3.4-alpha",
|
||||
"version": "2.4a0",
|
||||
"url": "https://github.com/PAIA-Playful-AI-Arena/easy_game",
|
||||
"description": "這是一個吃東西小遊戲,除了讓你熟習所有基本操作,也是 PAIA 的遊戲教學範例",
|
||||
"logo": [
|
||||
|
@ -18,7 +18,7 @@
|
|||
"type": "int",
|
||||
"min": -1,
|
||||
"max": 100,
|
||||
"default": -1,
|
||||
"default": 1,
|
||||
"help": "選定內建關卡,請注意,使用此設定將會覆蓋掉其他關卡設定,預設為 -1 不選擇任何關卡。"
|
||||
}, {
|
||||
"name": "level_file",
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
{
|
||||
"time_to_play": 300,
|
||||
"playground_size": [
|
||||
100,
|
||||
200
|
||||
],
|
||||
"playground_size_w":300,
|
||||
"playground_size_h":300,
|
||||
"score_to_pass": 10,
|
||||
"good_food_count": [3,0,0],
|
||||
"bad_food_count": [0,0,0]
|
||||
"food_1": 3,
|
||||
"food_2": 0,
|
||||
"food_3": 0,
|
||||
"garbage_1": 0,
|
||||
"garbage_2": 0,
|
||||
"garbage_3": 0
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
{
|
||||
"time_to_play": 300,
|
||||
"playground_size": [
|
||||
200,
|
||||
200
|
||||
],
|
||||
"score_to_pass": 15,
|
||||
"good_food_count": [5,0,0],
|
||||
"bad_food_count": [0,0,0]
|
||||
"playground_size_w":350,
|
||||
"playground_size_h":350,
|
||||
"score_to_pass": 10,
|
||||
"food_1": 5,
|
||||
"food_2": 0,
|
||||
"food_3": 0,
|
||||
"garbage_1": 0,
|
||||
"garbage_2": 0,
|
||||
"garbage_3": 0
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
{
|
||||
"time_to_play": 500,
|
||||
"playground_size": [
|
||||
300,
|
||||
300
|
||||
],
|
||||
"playground_size_w":350,
|
||||
"playground_size_h":350,
|
||||
"score_to_pass": 15,
|
||||
"good_food_count": [6,0,0],
|
||||
"bad_food_count": [2,0,0]
|
||||
"food_1": 3,
|
||||
"food_2": 0,
|
||||
"food_3": 0,
|
||||
"garbage_1": 3,
|
||||
"garbage_2": 0,
|
||||
"garbage_3": 0
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
{
|
||||
"time_to_play": 600,
|
||||
"playground_size": [
|
||||
300,
|
||||
300
|
||||
],
|
||||
"score_to_pass": 15,
|
||||
"good_food_count": [8,0,0],
|
||||
"bad_food_count": [4,0,0]
|
||||
"playground_size_w":400,
|
||||
"playground_size_h":400,
|
||||
"score_to_pass": 20,
|
||||
"food_1": 8,
|
||||
"food_2": 0,
|
||||
"food_3": 0,
|
||||
"garbage_1": 4,
|
||||
"garbage_2": 0,
|
||||
"garbage_3": 0
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
{
|
||||
"time_to_play": 800,
|
||||
"playground_size": [
|
||||
300,
|
||||
300
|
||||
],
|
||||
"score_to_pass": 15,
|
||||
"good_food_count": [8,3,0],
|
||||
"bad_food_count": [4,0,0]
|
||||
"playground_size_w":400,
|
||||
"playground_size_h":400,
|
||||
"score_to_pass": 30,
|
||||
"food_1": 8,
|
||||
"food_2": 3,
|
||||
"food_3": 0,
|
||||
"garbage_1": 3,
|
||||
"garbage_2": 0,
|
||||
"garbage_3": 0
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
{
|
||||
"time_to_play": 1000,
|
||||
"playground_size": [
|
||||
400,
|
||||
400
|
||||
],
|
||||
"score_to_pass": 15,
|
||||
"good_food_count": [8,4,0],
|
||||
"bad_food_count": [5,2,0]
|
||||
"playground_size_w":400,
|
||||
"playground_size_h":400,
|
||||
"score_to_pass": 30,
|
||||
"food_1": 8,
|
||||
"food_2": 4,
|
||||
"food_3": 0,
|
||||
"garbage_1": 5,
|
||||
"garbage_2": 2,
|
||||
"garbage_3": 0
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
{
|
||||
"time_to_play": 1000,
|
||||
"playground_size": [
|
||||
400,
|
||||
400
|
||||
],
|
||||
"score_to_pass": 20,
|
||||
"good_food_count": [8,4,2],
|
||||
"bad_food_count": [4,4,0]
|
||||
"time_to_play": 800,
|
||||
"playground_size_w":500,
|
||||
"playground_size_h":500,
|
||||
"score_to_pass": 30,
|
||||
"food_1": 8,
|
||||
"food_2": 4,
|
||||
"food_3": 2,
|
||||
"garbage_1": 4,
|
||||
"garbage_2": 2,
|
||||
"garbage_3": 0
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
{
|
||||
"time_to_play": 1200,
|
||||
"playground_size": [
|
||||
500,
|
||||
500
|
||||
],
|
||||
"score_to_pass": 20,
|
||||
"good_food_count": [6,4,2],
|
||||
"bad_food_count": [6,4,2]
|
||||
"playground_size_w":500,
|
||||
"playground_size_h":500,
|
||||
"score_to_pass": 50,
|
||||
"food_1": 6,
|
||||
"food_2": 4,
|
||||
"food_3": 2,
|
||||
"garbage_1": 6,
|
||||
"garbage_2": 4,
|
||||
"garbage_3": 2
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
{
|
||||
"time_to_play": 1200,
|
||||
"playground_size": [
|
||||
600,
|
||||
500
|
||||
],
|
||||
"score_to_pass": 25,
|
||||
"good_food_count": [10,5,3],
|
||||
"bad_food_count": [12,7,4]
|
||||
"playground_size_w":600,
|
||||
"playground_size_h":600,
|
||||
"score_to_pass": 50,
|
||||
"food_1": 6,
|
||||
"food_2": 5,
|
||||
"food_3": 3,
|
||||
"garbage_1": 8,
|
||||
"garbage_2": 8,
|
||||
"garbage_3": 5
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
{
|
||||
"time_to_play": 1200,
|
||||
"playground_size": [
|
||||
700,
|
||||
600
|
||||
],
|
||||
"score_to_pass": 25,
|
||||
"good_food_count": [12,8,4],
|
||||
"bad_food_count": [25,10,5]
|
||||
"playground_size_w":700,
|
||||
"playground_size_h":600,
|
||||
"score_to_pass": 50,
|
||||
"food_1": 12,
|
||||
"food_2": 8,
|
||||
"food_3": 4,
|
||||
"garbage_1": 25,
|
||||
"garbage_2": 10,
|
||||
"garbage_3": 8
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
{
|
||||
"time_to_play": 300,
|
||||
"playground_size": [
|
||||
100,
|
||||
200
|
||||
],
|
||||
"time_to_play": 600,
|
||||
"playground_size_w":650,
|
||||
"playground_size_h":550,
|
||||
"score_to_pass": 10,
|
||||
"good_food_count": [3,0,0],
|
||||
"bad_food_count": [0,0,0]
|
||||
"food_1": 3,
|
||||
"food_2": 2,
|
||||
"food_3": 5,
|
||||
"garbage_1": 1,
|
||||
"garbage_2": 1,
|
||||
"garbage_3": 1
|
||||
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
import random
|
||||
|
||||
import orjson
|
||||
import pygame
|
||||
|
||||
|
||||
|
@ -10,7 +12,7 @@ class MLPlay:
|
|||
"""
|
||||
Generate the command according to the received scene information
|
||||
"""
|
||||
# print("AI received data from game :", json.dumps(scene_info))
|
||||
# print("AI received data from game :", orjson.dumps(scene_info))
|
||||
# print(scene_info)
|
||||
actions = []
|
||||
|
||||
|
|
98
src/env.py
|
@ -3,44 +3,41 @@ from os import path
|
|||
|
||||
from mlgame.utils.enum import StringEnum
|
||||
# game
|
||||
WIDTH = 800
|
||||
WIDTH = 900
|
||||
HEIGHT = 600
|
||||
BG_COLOR = "#111111"
|
||||
BG_COLOR = "#2B2B49"
|
||||
PG_COLOR = "#B3E5FC"
|
||||
|
||||
# ball
|
||||
BALL_COLOR = "#FFEB3B"
|
||||
BALL_VEL = 10
|
||||
BALL_H = 30
|
||||
BALL_W = 30
|
||||
BALL_GROWTH_SCORE_STEP = 15
|
||||
BALL_GROWTH_SIZE_STEP=10
|
||||
BALL_GROWTH_VEL_STEP=3
|
||||
BALL_SIZE_MAX = 100
|
||||
BALL_SIZE_MIN = 10
|
||||
BALL_VEL_MAX = 25
|
||||
BALL_VEL_MIN = 10
|
||||
# ball -> squid
|
||||
# BALL_COLOR = "#FFEB3B"
|
||||
SQUID_W = 40
|
||||
SQUID_H = 60
|
||||
LEVEL_THRESHOLDS = [10, 30, 60, 100, 150,200]
|
||||
LEVEL_PROPERTIES = {
|
||||
1: {'size_ratio': 1.0, 'vel': 10},
|
||||
2: {'size_ratio': 1.2, 'vel': 12},
|
||||
3: {'size_ratio': 1.4, 'vel': 15},
|
||||
4: {'size_ratio': 1.6, 'vel': 18},
|
||||
5: {'size_ratio': 1.8, 'vel': 21},
|
||||
6: {'size_ratio': 2.0, 'vel': 25},
|
||||
}
|
||||
|
||||
|
||||
|
||||
ASSET_IMAGE_DIR = path.join(path.dirname(__file__), "../asset/img")
|
||||
# food
|
||||
class FoodTypeEnum(StringEnum):
|
||||
GOOD_1 = auto()
|
||||
GOOD_2 = auto()
|
||||
GOOD_3 = auto()
|
||||
BAD_1 = auto()
|
||||
BAD_2 = auto()
|
||||
BAD_3 = auto()
|
||||
FOOD_1 = auto()
|
||||
FOOD_2 = auto()
|
||||
FOOD_3 = auto()
|
||||
GARBAGE_1 = auto()
|
||||
GARBAGE_2 = auto()
|
||||
GARBAGE_3 = auto()
|
||||
|
||||
FOOD_COLOR_MAP = {
|
||||
FoodTypeEnum.GOOD_1: "#009688",
|
||||
FoodTypeEnum.GOOD_2: "#009688",
|
||||
FoodTypeEnum.GOOD_3: "#009688",
|
||||
FoodTypeEnum.BAD_1: "#FF1744",
|
||||
FoodTypeEnum.BAD_2: "#FF1744",
|
||||
FoodTypeEnum.BAD_3: "#FF1744"
|
||||
}
|
||||
FOOD_LV1_SIZE = 8
|
||||
FOOD_LV2_SIZE = 12
|
||||
FOOD_LV3_SIZE = 16
|
||||
|
||||
FOOD_LV1_SIZE = 30
|
||||
FOOD_LV2_SIZE = 40
|
||||
FOOD_LV3_SIZE = 50
|
||||
|
||||
# path of assets
|
||||
ASSET_PATH = path.join(path.dirname(__file__), "..", "asset")
|
||||
|
@ -48,4 +45,43 @@ LEVEL_PATH = path.join(path.dirname(__file__), "..", "levels")
|
|||
SOUND_PATH = path.join(path.dirname(__file__), "..", "asset", "sounds")
|
||||
MUSIC_PATH = path.join(path.dirname(__file__), "..", "asset", "music")
|
||||
|
||||
BG_PATH = path.join(ASSET_IMAGE_DIR, "background.png")
|
||||
SQUID_PATH = path.join(ASSET_IMAGE_DIR, "squid.png")
|
||||
|
||||
IMG_ID_FOOD01_L = "food_01_L"
|
||||
IMG_ID_FOOD02_L = "food_02_L"
|
||||
IMG_ID_FOOD03_L = "food_03_L"
|
||||
IMG_ID_FOOD01_R = "food_01_R"
|
||||
IMG_ID_FOOD02_R = "food_02_R"
|
||||
IMG_ID_FOOD03_R = "food_03_R"
|
||||
|
||||
FOOD01_L_PATH = path.join(ASSET_IMAGE_DIR, "food_01_L.png")
|
||||
FOOD02_L_PATH = path.join(ASSET_IMAGE_DIR, "food_02_L.png")
|
||||
FOOD03_L_PATH = path.join(ASSET_IMAGE_DIR, "food_03_L.png")
|
||||
FOOD01_R_PATH = path.join(ASSET_IMAGE_DIR, "food_01_R.png")
|
||||
FOOD02_R_PATH = path.join(ASSET_IMAGE_DIR, "food_02_R.png")
|
||||
FOOD03_R_PATH = path.join(ASSET_IMAGE_DIR, "food_03_R.png")
|
||||
|
||||
GARBAGE01_PATH = path.join(ASSET_IMAGE_DIR, "garbage_01.png")
|
||||
GARBAGE02_PATH = path.join(ASSET_IMAGE_DIR, "garbage_02.png")
|
||||
GARBAGE03_PATH = path.join(ASSET_IMAGE_DIR, "garbage_03.png")
|
||||
|
||||
|
||||
ASSET_IMG_URL = "https://raw.githubusercontent.com/PAIA-Playful-AI-Arena/easy_game/main/asset/img/"
|
||||
BG_URL = ASSET_IMG_URL + "background.png"
|
||||
SQUID_URL = ASSET_IMG_URL + "squid.png"
|
||||
# Food URLs
|
||||
FOOD01_L_URL = ASSET_IMG_URL + "food_01_L.png"
|
||||
FOOD02_L_URL = ASSET_IMG_URL + "food_02_L.png" # Assuming the naming pattern is similar
|
||||
FOOD03_L_URL = ASSET_IMG_URL + "food_03_L.png"
|
||||
FOOD01_R_URL = ASSET_IMG_URL + "food_01_R.png"
|
||||
FOOD02_R_URL = ASSET_IMG_URL + "food_02_R.png"
|
||||
FOOD03_R_URL = ASSET_IMG_URL + "food_03_R.png"
|
||||
|
||||
# Garbage URLs
|
||||
GARBAGE01_URL = ASSET_IMG_URL + "garbage_01.png"
|
||||
GARBAGE02_URL = ASSET_IMG_URL + "garbage_02.png"
|
||||
GARBAGE03_URL = ASSET_IMG_URL + "garbage_03.png"
|
||||
# BAR_URL = "https://raw.githubusercontent.com/PAIA/dont_touch/master/asset/image/bar.png"
|
||||
|
||||
# https://raw.githubusercontent.com/PAIA-Playful-AI-Arena/easy_game/main/asset/img/background.jpg
|
188
src/foods.py
|
@ -1,92 +1,162 @@
|
|||
import pygame.sprite
|
||||
import random
|
||||
|
||||
from .env import FoodTypeEnum, FOOD_COLOR_MAP, FOOD_LV1_SIZE, FOOD_LV2_SIZE, FOOD_LV3_SIZE
|
||||
from mlgame.view.view_model import create_rect_view_data
|
||||
import pygame.sprite
|
||||
from pygame import Rect
|
||||
|
||||
from mlgame.view.view_model import create_image_view_data
|
||||
from .env import *
|
||||
|
||||
FOOD1_VEL = 1
|
||||
FOOD2_VEL = 2
|
||||
FOOD3_VEL = 4
|
||||
|
||||
|
||||
class Food(pygame.sprite.Sprite):
|
||||
def __init__(self, group):
|
||||
def __init__(self, group, type: FoodTypeEnum, image_id: str, image_size=None, score: int = 1):
|
||||
pygame.sprite.Sprite.__init__(self, group)
|
||||
self.image = pygame.Surface([8, 8])
|
||||
self.type = None
|
||||
self.score = 0
|
||||
self.color = None
|
||||
|
||||
if image_size is None:
|
||||
image_size = [FOOD_LV1_SIZE, FOOD_LV1_SIZE]
|
||||
self.image = pygame.Surface(image_size)
|
||||
self.type = type
|
||||
self.score = score
|
||||
self.rect = self.image.get_rect()
|
||||
self.angle = 0
|
||||
self.rect_float_x = 0
|
||||
self.rect_float_y = 0
|
||||
self.image_id = image_id
|
||||
|
||||
def update(self) -> None:
|
||||
def update(self, *args, **kwargs) -> None:
|
||||
pass
|
||||
|
||||
@property
|
||||
def game_object_data(self):
|
||||
return create_rect_view_data(
|
||||
"food",
|
||||
return create_image_view_data(
|
||||
self.image_id,
|
||||
self.rect.x,
|
||||
self.rect.y,
|
||||
self.rect.width,
|
||||
self.rect.height,
|
||||
self.color
|
||||
)
|
||||
|
||||
def set_center_x_and_y(self, x: int, y: int):
|
||||
self.rect.centerx = x
|
||||
self.rect.centery = y
|
||||
self.rect_float_x = self.rect.centerx
|
||||
self.rect_float_y = self.rect.centery
|
||||
|
||||
class GoodFoodLv1(Food):
|
||||
|
||||
class Food1(Food):
|
||||
def __init__(self, group):
|
||||
super().__init__(group)
|
||||
self.image = pygame.Surface([FOOD_LV1_SIZE, FOOD_LV1_SIZE])
|
||||
self.type = FoodTypeEnum.GOOD_1
|
||||
self.color = FOOD_COLOR_MAP[self.type]
|
||||
self.score = 1
|
||||
self.rect = self.image.get_rect()
|
||||
class GoodFoodLv2(Food):
|
||||
super().__init__(group, FoodTypeEnum.FOOD_1, IMG_ID_FOOD01_L, [FOOD_LV1_SIZE, FOOD_LV1_SIZE],
|
||||
1)
|
||||
self._vel = FOOD1_VEL * random.choice([-1,1])
|
||||
|
||||
self.image_id= IMG_ID_FOOD01_R if self._vel > 0 else IMG_ID_FOOD01_L
|
||||
|
||||
def update(self, playground: Rect, squid: pygame.sprite.Sprite):
|
||||
|
||||
self.rect_float_x += self._vel
|
||||
self.rect_float_y += random.choice([-0.3, -0.5, -0.7, 0, 0.3, 0.5, 0.7])
|
||||
self.rect.centerx = self.rect_float_x
|
||||
self.rect.centery = self.rect_float_y
|
||||
|
||||
if self.rect.left < playground.left and self._vel < 0.0:
|
||||
self._vel = FOOD1_VEL
|
||||
self.image_id = IMG_ID_FOOD01_R
|
||||
elif self.rect.right > playground.right and self._vel > 0.0:
|
||||
self._vel = - FOOD1_VEL
|
||||
self.image_id = IMG_ID_FOOD01_L
|
||||
|
||||
|
||||
class Food2(Food):
|
||||
def __init__(self, group):
|
||||
super().__init__(group)
|
||||
super().__init__(group, FoodTypeEnum.FOOD_2, IMG_ID_FOOD02_L, [FOOD_LV2_SIZE, FOOD_LV2_SIZE], 2)
|
||||
self._vel = FOOD2_VEL * random.choice([-1, 1])
|
||||
self.image_id = IMG_ID_FOOD02_R if self._vel > 0 else IMG_ID_FOOD02_L
|
||||
|
||||
self.image = pygame.Surface([FOOD_LV2_SIZE, FOOD_LV2_SIZE])
|
||||
self.type = FoodTypeEnum.GOOD_2
|
||||
self.color = FOOD_COLOR_MAP[self.type]
|
||||
self.score = 2
|
||||
self.rect = self.image.get_rect()
|
||||
def update(self, playground: Rect, squid: pygame.sprite.Sprite):
|
||||
self.rect_float_x += self._vel
|
||||
self.rect_float_y += random.choice([-0.5, -0.7, -1, -1.3, 0, 1, 1.3, 0.3, 0.5, 0.7])
|
||||
self.rect.centerx = self.rect_float_x
|
||||
self.rect.centery = self.rect_float_y
|
||||
|
||||
class GoodFoodLv3(Food):
|
||||
if self.rect.left < playground.left and self._vel < 0.0:
|
||||
self._vel = FOOD2_VEL
|
||||
self.image_id = IMG_ID_FOOD02_R
|
||||
elif self.rect.right > playground.right and self._vel > 0.0:
|
||||
self._vel = -FOOD2_VEL
|
||||
self.image_id = IMG_ID_FOOD02_L
|
||||
|
||||
class Food3(Food):
|
||||
def __init__(self, group):
|
||||
super().__init__(group)
|
||||
super().__init__(group, FoodTypeEnum.FOOD_3, IMG_ID_FOOD03_L, [FOOD_LV3_SIZE, FOOD_LV3_SIZE], 4)
|
||||
self._vel = FOOD3_VEL * random.choice([-1, 1])
|
||||
self.image_id = IMG_ID_FOOD03_R if self._vel > 0 else IMG_ID_FOOD03_L
|
||||
|
||||
self.image = pygame.Surface([FOOD_LV3_SIZE, FOOD_LV3_SIZE])
|
||||
self.type = FoodTypeEnum.GOOD_3
|
||||
self.color = FOOD_COLOR_MAP[self.type]
|
||||
self.score = 4
|
||||
self.rect = self.image.get_rect()
|
||||
def update(self, playground: Rect, squid: pygame.sprite.Sprite):
|
||||
self.rect_float_x += self._vel
|
||||
self.rect_float_y += random.choice([-0.7, -1, -1.3, -1.7, 0, 1.7, 1, 1.3, 0.3, 0.7])
|
||||
self.rect.centerx = self.rect_float_x
|
||||
self.rect.centery = self.rect_float_y
|
||||
|
||||
|
||||
|
||||
|
||||
class BadFoodLv1(Food):
|
||||
if self.rect.left < playground.left and self._vel < 0.0:
|
||||
self._vel = FOOD3_VEL
|
||||
self.image_id = IMG_ID_FOOD03_R
|
||||
elif self.rect.right > playground.right and self._vel > 0.0:
|
||||
self._vel = -FOOD3_VEL
|
||||
self.image_id = IMG_ID_FOOD03_L
|
||||
class Garbage1(Food):
|
||||
def __init__(self, group):
|
||||
super().__init__(group)
|
||||
self.image = pygame.Surface([FOOD_LV1_SIZE, FOOD_LV1_SIZE])
|
||||
self.type = FoodTypeEnum.BAD_1
|
||||
self.color = FOOD_COLOR_MAP[self.type]
|
||||
self.score = -1
|
||||
self.rect = self.image.get_rect()
|
||||
self.angle = 0
|
||||
super().__init__(group, FoodTypeEnum.GARBAGE_1, "garbage01",
|
||||
[FOOD_LV1_SIZE, FOOD_LV1_SIZE], -1)
|
||||
self._vel = FOOD1_VEL
|
||||
|
||||
class BadFoodLv2(Food):
|
||||
def update(self, playground: Rect, squid: pygame.sprite.Sprite):
|
||||
self.rect_float_x += random.choice([-0.3, -0.5, -0.7, 0, 0.3, 0.5, 0.7])
|
||||
self.rect_float_y += self._vel
|
||||
self.rect.centerx = self.rect_float_x
|
||||
self.rect.centery = self.rect_float_y
|
||||
|
||||
if self.rect.top > playground.bottom:
|
||||
self.rect.y = playground.top
|
||||
self.rect_float_y = self.rect.centery
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class Garbage2(Food):
|
||||
def __init__(self, group):
|
||||
super().__init__(group)
|
||||
super().__init__(group, FoodTypeEnum.GARBAGE_2, "garbage02",
|
||||
[FOOD_LV2_SIZE, FOOD_LV2_SIZE], -4)
|
||||
self._vel = FOOD2_VEL
|
||||
|
||||
self.image = pygame.Surface([FOOD_LV2_SIZE, FOOD_LV2_SIZE])
|
||||
self.type = FoodTypeEnum.BAD_2
|
||||
self.color = FOOD_COLOR_MAP[self.type]
|
||||
self.score = -2
|
||||
self.rect = self.image.get_rect()
|
||||
def update(self, playground: Rect, squid: pygame.sprite.Sprite):
|
||||
self.rect_float_x += random.choice([-0.5, -0.7, -1, -1.3, 0, 1, 1.3, 0.3, 0.5, 0.7])
|
||||
self.rect_float_y += self._vel
|
||||
self.rect.centerx = self.rect_float_x
|
||||
self.rect.centery = self.rect_float_y
|
||||
|
||||
class BadFoodLv3(Food):
|
||||
if self.rect.top > playground.bottom:
|
||||
self.rect.y = playground.top
|
||||
self.rect_float_y = self.rect.centery
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class Garbage3(Food):
|
||||
def __init__(self, group):
|
||||
super().__init__(group)
|
||||
super().__init__(group, FoodTypeEnum.GARBAGE_3, "garbage03",
|
||||
[FOOD_LV3_SIZE, FOOD_LV3_SIZE], -10)
|
||||
self._vel = FOOD1_VEL
|
||||
|
||||
self.image = pygame.Surface([FOOD_LV3_SIZE, FOOD_LV3_SIZE])
|
||||
self.type = FoodTypeEnum.BAD_3
|
||||
self.color = FOOD_COLOR_MAP[self.type]
|
||||
self.score = -4
|
||||
self.rect = self.image.get_rect()
|
||||
def update(self, playground: Rect, squid: pygame.sprite.Sprite):
|
||||
self.rect_float_x += random.choice([-0.7, -1, -1.3, -1.7, 0, 1.7, 1, 1.3, 0.3, 0.7])
|
||||
self.rect_float_y += self._vel
|
||||
self.rect.centerx = self.rect_float_x
|
||||
self.rect.centery = self.rect_float_y
|
||||
|
||||
if self.rect.top > playground.bottom:
|
||||
self.rect.y = playground.top
|
||||
self.rect_float_y = self.rect.centery
|
||||
|
||||
pass
|
||||
|
|
150
src/game.py
|
@ -10,11 +10,11 @@ from mlgame.view.decorator import check_game_progress, check_game_result
|
|||
from mlgame.view.view_model import *
|
||||
from .env import *
|
||||
from .foods import *
|
||||
from .game_object import Ball
|
||||
from .game_object import Squid, LevelParams
|
||||
from .sound_controller import SoundController
|
||||
|
||||
|
||||
def revise_ball(ball: Ball, playground: pygame.Rect):
|
||||
def revise_ball(ball: Squid, playground: pygame.Rect):
|
||||
ball_rect = copy.deepcopy(ball.rect)
|
||||
if ball_rect.left < playground.left:
|
||||
ball_rect.left = playground.left
|
||||
|
@ -50,57 +50,45 @@ class EasyGame(PaiaGame):
|
|||
|
||||
self._init_game()
|
||||
|
||||
|
||||
def _init_game_by_file(self, level_file_path: str):
|
||||
try:
|
||||
with open(level_file_path) as f:
|
||||
game_params = json.load(f)
|
||||
game_params = LevelParams(**json.load(f))
|
||||
|
||||
except:
|
||||
# If the file doesn't exist, use default parameters
|
||||
print("此關卡檔案不存在,遊戲將會會自動使用第一關檔案 001.json。")
|
||||
print("This level file is not existed , game will load 001.json automatically.")
|
||||
with open(os.path.join(LEVEL_PATH, "001.json")) as f:
|
||||
game_params = json.load(f)
|
||||
game_params = LevelParams(**json.load(f))
|
||||
self._level = 1
|
||||
self._level_file = ""
|
||||
finally:
|
||||
# set game params
|
||||
self._playground_w = int(game_params["playground_size"][0])
|
||||
self._playground_h = int(game_params["playground_size"][1])
|
||||
self.playground = pygame.Rect(
|
||||
0, 0,
|
||||
self._playground_w,
|
||||
self._playground_h
|
||||
game_params.playground_size_w,
|
||||
game_params.playground_size_h
|
||||
)
|
||||
self._good_food_count = game_params["good_food_count"]
|
||||
self._bad_food_count = game_params["bad_food_count"]
|
||||
self._score_to_pass = int(game_params["score_to_pass"])
|
||||
self._frame_limit = int(game_params["time_to_play"])
|
||||
self.playground.center = (WIDTH / 2, HEIGHT / 2)
|
||||
|
||||
self._score_to_pass = game_params.score_to_pass
|
||||
self._frame_limit = game_params.time_to_play
|
||||
self.playground.center = ((WIDTH-200) / 2, HEIGHT / 2)
|
||||
|
||||
# init game
|
||||
self.ball = Ball()
|
||||
self.squid = Squid()
|
||||
self.foods.empty()
|
||||
|
||||
if not isinstance(self._good_food_count,list) or len(self._good_food_count)<3:
|
||||
raise Exception("你的關卡檔案格式有誤,請在'good_food_count' 欄位後面填入一個長度為3的陣列,舉例: [1,2,3]")
|
||||
elif not isinstance(self._bad_food_count, list) or len(self._bad_food_count) < 3:
|
||||
raise Exception("你的關卡檔案格式有誤,請在'bad_food_count' 欄位後面填入一個長度為3的陣列,舉例: [1,2,3]")
|
||||
|
||||
else:
|
||||
self._create_foods(GoodFoodLv1, self._good_food_count[0])
|
||||
self._create_foods(GoodFoodLv2, self._good_food_count[1])
|
||||
self._create_foods(GoodFoodLv3, self._good_food_count[2])
|
||||
self._create_foods(BadFoodLv1, self._bad_food_count[0])
|
||||
self._create_foods(BadFoodLv2, self._bad_food_count[1])
|
||||
self._create_foods(BadFoodLv3, self._bad_food_count[2])
|
||||
self._create_foods(Food1, game_params.food_1)
|
||||
self._create_foods(Food2, game_params.food_2)
|
||||
self._create_foods(Food3, game_params.food_3)
|
||||
self._create_foods(Garbage1, game_params.garbage_1)
|
||||
self._create_foods(Garbage2, game_params.garbage_2)
|
||||
self._create_foods(Garbage3, game_params.garbage_3)
|
||||
|
||||
self.frame_count = 0
|
||||
self._frame_count_down = self._frame_limit
|
||||
self.sound_controller.play_music()
|
||||
|
||||
|
||||
|
||||
def update(self, commands):
|
||||
# handle command
|
||||
ai_1p_cmd = commands[get_ai_name(0)]
|
||||
|
@ -109,10 +97,10 @@ class EasyGame(PaiaGame):
|
|||
else:
|
||||
action = "NONE"
|
||||
|
||||
self.ball.update(action)
|
||||
revise_ball(self.ball, self.playground)
|
||||
self.squid.update(action)
|
||||
revise_ball(self.squid, self.playground)
|
||||
# update sprite
|
||||
self.foods.update()
|
||||
self.foods.update(playground=self.playground, squid=self.squid)
|
||||
|
||||
# handle collision
|
||||
|
||||
|
@ -127,16 +115,16 @@ class EasyGame(PaiaGame):
|
|||
return "RESET"
|
||||
|
||||
def _check_foods_collision(self):
|
||||
hits = pygame.sprite.spritecollide(self.ball, self.foods, True)
|
||||
hits = pygame.sprite.spritecollide(self.squid, self.foods, True)
|
||||
if hits:
|
||||
for food in hits:
|
||||
# self.ball.score += food.score
|
||||
# growth play special sound
|
||||
self.ball.eat_food_and_change_level_and_play_sound(food,self.sound_controller)
|
||||
self.squid.eat_food_and_change_level_and_play_sound(food, self.sound_controller)
|
||||
self._create_foods(food.__class__, 1)
|
||||
if isinstance(food, (GoodFoodLv1,GoodFoodLv2,GoodFoodLv3,)):
|
||||
if isinstance(food, (Food1, Food2, Food3,)):
|
||||
self.sound_controller.play_eating_good()
|
||||
elif isinstance(food, (BadFoodLv1,BadFoodLv2,BadFoodLv3,)):
|
||||
elif isinstance(food, (Garbage1, Garbage2, Garbage3,)):
|
||||
self.sound_controller.play_eating_bad()
|
||||
|
||||
def get_data_from_game_to_player(self):
|
||||
|
@ -147,20 +135,23 @@ class EasyGame(PaiaGame):
|
|||
to_players_data = {}
|
||||
foods_data = []
|
||||
for food in self.foods:
|
||||
# TODO 確認要提供中心點座標還是左上角座標。
|
||||
foods_data.append({"x": food.rect.x, "y": food.rect.y, "type": food.type, "score": food.score})
|
||||
foods_data.append(
|
||||
{"x": food.rect.centerx, "y": food.rect.centery,
|
||||
"w":food.rect.width,"h":food.rect.height,
|
||||
"type": str(food.type), "score": food.score}
|
||||
)
|
||||
|
||||
data_to_1p = {
|
||||
"frame": self.frame_count,
|
||||
# TODO 確認要提供中心點座標還是左上角座標。
|
||||
"player_x": self.ball.rect.centerx,
|
||||
"player_y": self.ball.rect.centery,
|
||||
"player_size":self.ball.rect.width,
|
||||
"player_vel":self.ball.vel,
|
||||
"squid_x": self.squid.rect.centerx,
|
||||
"squid_y": self.squid.rect.centery,
|
||||
"squid_w": self.squid.rect.width,
|
||||
"squid_h": self.squid.rect.height,
|
||||
"squid_vel": self.squid.vel,
|
||||
"squid_lv": self.squid.lv,
|
||||
"foods": foods_data,
|
||||
|
||||
"score": self.ball.score,
|
||||
"score_to_pass":self._score_to_pass,
|
||||
"score": self.squid.score,
|
||||
"score_to_pass": self._score_to_pass,
|
||||
"status": self.get_game_status()
|
||||
|
||||
}
|
||||
|
@ -185,11 +176,10 @@ class EasyGame(PaiaGame):
|
|||
if self.is_passed:
|
||||
self._level += 1
|
||||
self.sound_controller.play_cheer()
|
||||
|
||||
else:
|
||||
self.sound_controller.play_fail()
|
||||
self._init_game()
|
||||
|
||||
|
||||
|
||||
pass
|
||||
|
||||
def _init_game(self):
|
||||
|
@ -203,7 +193,7 @@ class EasyGame(PaiaGame):
|
|||
|
||||
@property
|
||||
def is_passed(self):
|
||||
return self.ball.score > self._score_to_pass
|
||||
return self.squid.score > self._score_to_pass
|
||||
|
||||
@property
|
||||
def is_running(self):
|
||||
|
@ -217,12 +207,29 @@ class EasyGame(PaiaGame):
|
|||
# background = create_asset_init_data(
|
||||
# "background", WIDTH, HEIGHT, bg_path,
|
||||
# github_raw_url="https://raw.githubusercontent.com/PAIA-Playful-AI-Arena/easy_game/main/asset/img/background.jpg")
|
||||
scene_init_data = {"scene": self.scene.__dict__,
|
||||
"assets": [
|
||||
# background
|
||||
],
|
||||
# "audios": {}
|
||||
}
|
||||
|
||||
scene_init_data = {
|
||||
"scene": self.scene.__dict__,
|
||||
"assets": [
|
||||
create_asset_init_data("bg", 1000, 1000, BG_PATH, BG_URL),
|
||||
create_asset_init_data("squid", SQUID_W, SQUID_H, SQUID_PATH, SQUID_URL),
|
||||
create_asset_init_data(IMG_ID_FOOD01_L, FOOD_LV1_SIZE, FOOD_LV1_SIZE, FOOD01_L_PATH, FOOD01_L_URL),
|
||||
create_asset_init_data(IMG_ID_FOOD02_L, FOOD_LV2_SIZE, FOOD_LV2_SIZE, FOOD02_L_PATH, FOOD02_L_URL),
|
||||
create_asset_init_data(IMG_ID_FOOD03_L, FOOD_LV3_SIZE, FOOD_LV3_SIZE, FOOD03_L_PATH, FOOD03_L_URL),
|
||||
create_asset_init_data(IMG_ID_FOOD01_R, FOOD_LV1_SIZE, FOOD_LV1_SIZE, FOOD01_R_PATH, FOOD01_R_URL),
|
||||
create_asset_init_data(IMG_ID_FOOD02_R, FOOD_LV2_SIZE, FOOD_LV2_SIZE, FOOD02_R_PATH, FOOD02_R_URL),
|
||||
create_asset_init_data(IMG_ID_FOOD03_R, FOOD_LV3_SIZE, FOOD_LV3_SIZE, FOOD03_R_PATH, FOOD03_R_URL),
|
||||
create_asset_init_data("garbage01", FOOD_LV1_SIZE,FOOD_LV1_SIZE, GARBAGE01_PATH, GARBAGE01_URL),
|
||||
create_asset_init_data("garbage02", FOOD_LV2_SIZE,FOOD_LV2_SIZE, GARBAGE02_PATH, GARBAGE02_URL),
|
||||
create_asset_init_data("garbage03", FOOD_LV3_SIZE,FOOD_LV3_SIZE, GARBAGE03_PATH, GARBAGE03_URL),
|
||||
],
|
||||
"background": [
|
||||
# create_image_view_data(
|
||||
# 'bg', self.playground.x, self.playground.y,
|
||||
# self.playground.w, self.playground.h)
|
||||
]
|
||||
# "audios": {}
|
||||
}
|
||||
return scene_init_data
|
||||
|
||||
@check_game_progress
|
||||
|
@ -233,21 +240,27 @@ class EasyGame(PaiaGame):
|
|||
foods_data = []
|
||||
for food in self.foods:
|
||||
foods_data.append(food.game_object_data)
|
||||
game_obj_list = [self.ball.game_object_data]
|
||||
game_obj_list = [self.squid.game_object_data]
|
||||
game_obj_list.extend(foods_data)
|
||||
backgrounds = [
|
||||
# create_image_view_data("background", 0, 0, WIDTH, HEIGHT),
|
||||
create_rect_view_data(
|
||||
"playground", self.playground.x, self.playground.y,
|
||||
self.playground.w, self.playground.h, PG_COLOR)
|
||||
# create_rect_view_data(
|
||||
# "playground", self.playground.x, self.playground.y,
|
||||
# self.playground.w, self.playground.h, PG_COLOR)
|
||||
create_image_view_data(
|
||||
'bg', self.playground.x, self.playground.y,
|
||||
self.playground.w, self.playground.h)
|
||||
]
|
||||
foregrounds = [
|
||||
|
||||
]
|
||||
toggle_objs = [
|
||||
create_text_view_data(f"Score:{self.ball.score:04d}", 600, 50, "#A5D6A7", "24px Arial BOLD"),
|
||||
create_text_view_data(f" Next:{self._score_to_pass:04d}", 600, 100, "#FF4081", "24px Arial BOLD"),
|
||||
create_text_view_data(f" Time:{self._frame_count_down:04d}", 600, 150, "#FF5722", "24px Arial BOLD"),
|
||||
create_text_view_data(f"Lv : {self.squid.lv:4d}", 720, 50, "#EEEEEE", "24px Arial BOLD"),
|
||||
create_text_view_data(f"Vel : {self.squid.vel:4d}", 720, 80, "#EEEEEE", "24px Arial BOLD"),
|
||||
create_text_view_data(f"Score: {self.squid.score:04d}", 720, 110, "#EEEEEE", "24px Arial BOLD"),
|
||||
create_text_view_data(f"Lv_up: {LEVEL_THRESHOLDS[self.squid.lv-1]:4d}", 720, 140, "#EEEEEE", "24px Arial BOLD"),
|
||||
create_text_view_data(f"Time : {self._frame_count_down:04d}", 720, 200, "#EEEEEE", "24px Arial BOLD"),
|
||||
create_text_view_data(f"ToPass: {self._score_to_pass:04d}", 720, 230, "#EEEEEE", "24px Arial BOLD"),
|
||||
|
||||
]
|
||||
scene_progress = create_scene_progress_data(frame=self.frame_count, background=backgrounds,
|
||||
|
@ -268,7 +281,7 @@ class EasyGame(PaiaGame):
|
|||
{
|
||||
"player": get_ai_name(0),
|
||||
"rank": 1,
|
||||
"score": self.ball.score,
|
||||
"score": self.squid.score,
|
||||
"passed": self.is_passed
|
||||
}
|
||||
]
|
||||
|
@ -297,6 +310,9 @@ class EasyGame(PaiaGame):
|
|||
for i in range(count):
|
||||
# add food to group
|
||||
food = FOOD_TYPE(self.foods)
|
||||
food.rect.centerx = random.randint(self.playground.left, self.playground.right)
|
||||
food.rect.centery = random.randint(self.playground.top, self.playground.bottom)
|
||||
food.set_center_x_and_y(
|
||||
random.randint(self.playground.left, self.playground.right),
|
||||
random.randint(self.playground.top, self.playground.bottom)
|
||||
)
|
||||
|
||||
pass
|
||||
|
|
|
@ -1,25 +1,49 @@
|
|||
import math
|
||||
|
||||
from typing import List
|
||||
|
||||
import pydantic
|
||||
import pygame.sprite
|
||||
|
||||
from .env import BALL_COLOR, BALL_VEL, BALL_H, BALL_W, BALL_GROWTH_SCORE_STEP, BALL_GROWTH_SIZE_STEP, \
|
||||
BALL_SIZE_MAX, BALL_GROWTH_VEL_STEP, BALL_VEL_MAX, BALL_SIZE_MIN, BALL_VEL_MIN
|
||||
from .env import *
|
||||
from .foods import Food
|
||||
from .sound_controller import SoundController
|
||||
from mlgame.view.view_model import create_rect_view_data
|
||||
from mlgame.view.view_model import create_rect_view_data, create_image_view_data
|
||||
|
||||
|
||||
class Ball(pygame.sprite.Sprite):
|
||||
class LevelParams(pydantic.BaseModel):
|
||||
|
||||
playground_size_w: int = 300
|
||||
playground_size_h: int = 300
|
||||
score_to_pass: int = 10
|
||||
time_to_play: int = 300
|
||||
|
||||
food_1: int = 3
|
||||
food_2: int = 0
|
||||
food_3: int = 0
|
||||
garbage_1: int = 0
|
||||
garbage_2: int = 0
|
||||
garbage_3: int = 0
|
||||
|
||||
|
||||
# level_thresholds = [10, 15, 20, 25, 30]
|
||||
|
||||
|
||||
class Squid(pygame.sprite.Sprite):
|
||||
ANGLE_TO_RIGHT = math.radians(-10)
|
||||
ANGLE_TO_LEFT = math.radians(10)
|
||||
|
||||
def __init__(self):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.origin_image = pygame.Surface([BALL_W, BALL_H])
|
||||
|
||||
self.origin_image = pygame.Surface([SQUID_W, SQUID_H])
|
||||
self.image = self.origin_image
|
||||
self.color = BALL_COLOR
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.center = (400, 300)
|
||||
self.rect.center = (350, 300)
|
||||
self._score = 0
|
||||
self._vel = BALL_VEL
|
||||
self._lv =1
|
||||
self._vel = LEVEL_PROPERTIES[1]['vel']
|
||||
self._lv = 1
|
||||
self.angle = 0
|
||||
|
||||
def update(self, motion):
|
||||
# for motion in motions:
|
||||
|
@ -29,11 +53,12 @@ class Ball(pygame.sprite.Sprite):
|
|||
self.rect.centery += self._vel
|
||||
elif motion == "LEFT":
|
||||
self.rect.centerx -= self._vel
|
||||
# self.angle += 5
|
||||
self.angle = self.ANGLE_TO_LEFT
|
||||
elif motion == "RIGHT":
|
||||
self.rect.centerx += self._vel
|
||||
# self.angle -= 5
|
||||
|
||||
self.angle = self.ANGLE_TO_RIGHT
|
||||
else:
|
||||
self.angle = 0
|
||||
# self.image = pygame.transform.rotate(self.origin_image, self.angle)
|
||||
# print(self.angle)
|
||||
# center = self.rect.center
|
||||
|
@ -42,31 +67,52 @@ class Ball(pygame.sprite.Sprite):
|
|||
|
||||
@property
|
||||
def game_object_data(self):
|
||||
return create_rect_view_data(
|
||||
"ball",
|
||||
|
||||
return create_image_view_data(
|
||||
"squid",
|
||||
self.rect.x,
|
||||
self.rect.y,
|
||||
self.rect.width,
|
||||
self.rect.height,
|
||||
self.color
|
||||
self.angle
|
||||
|
||||
)
|
||||
|
||||
def eat_food_and_change_level_and_play_sound(self, food: Food,sound_controller:SoundController):
|
||||
def eat_food_and_change_level_and_play_sound(self, food: Food, sound_controller: SoundController):
|
||||
self._score += food.score
|
||||
new_lv = math.ceil((self._score-BALL_GROWTH_SCORE_STEP + 1) / BALL_GROWTH_SCORE_STEP)
|
||||
self.rect.width = max(BALL_SIZE_MIN,min(BALL_W + new_lv * BALL_GROWTH_SIZE_STEP, BALL_SIZE_MAX))
|
||||
self.rect.height = max(BALL_SIZE_MIN,min(BALL_H + new_lv * BALL_GROWTH_SIZE_STEP, BALL_SIZE_MAX))
|
||||
self._vel = max(BALL_VEL_MIN,min(BALL_VEL + new_lv * BALL_GROWTH_VEL_STEP, BALL_VEL_MAX))
|
||||
new_lv = get_current_level(self._score)
|
||||
|
||||
if new_lv > self._lv:
|
||||
sound_controller.play_lv_up()
|
||||
elif new_lv < self._lv:
|
||||
sound_controller.play_lv_down()
|
||||
self._lv=new_lv
|
||||
pass
|
||||
if new_lv != self._lv:
|
||||
self.rect.width = SQUID_W * LEVEL_PROPERTIES[new_lv]['size_ratio']
|
||||
self.rect.height = SQUID_H * LEVEL_PROPERTIES[new_lv]['size_ratio']
|
||||
self._vel = LEVEL_PROPERTIES[new_lv]['vel']
|
||||
self._lv = new_lv
|
||||
|
||||
@property
|
||||
def score(self):
|
||||
return self._score
|
||||
|
||||
@property
|
||||
def vel(self):
|
||||
return self._vel
|
||||
@property
|
||||
def lv(self):
|
||||
return self._lv
|
||||
|
||||
|
||||
def get_current_level(score: int) -> int:
|
||||
"""
|
||||
Determines the current level based on the player's score.
|
||||
|
||||
:param score: int - The current score of the player.
|
||||
:return: int - The current level of the player.
|
||||
"""
|
||||
|
||||
for level, threshold in enumerate(LEVEL_THRESHOLDS, start=1):
|
||||
if score < threshold:
|
||||
return min(level,6)
|
||||
return len(LEVEL_THRESHOLDS) # Return the next level if score is beyond all thresholds
|
||||
|
|
|
@ -22,11 +22,12 @@ class SoundController():
|
|||
pygame.mixer.init()
|
||||
pygame.mixer.music.load(path.join(MUSIC_PATH, "bgm.wav"))
|
||||
pygame.mixer.music.set_volume(0.6)
|
||||
self._eating_good = pygame.mixer.Sound(path.join(SOUND_PATH, "Coin.wav"))
|
||||
self._eating_bad = pygame.mixer.Sound(path.join(SOUND_PATH, "Low Boing.wav"))
|
||||
self._cheer = pygame.mixer.Sound(path.join(SOUND_PATH, "Cheer.wav"))
|
||||
self._lv_up = pygame.mixer.Sound(path.join(SOUND_PATH, "lv_up.wav"))
|
||||
self._lv_down = pygame.mixer.Sound(path.join(SOUND_PATH, "lv_down.wav"))
|
||||
self._eating_good = pygame.mixer.Sound(path.join(SOUND_PATH, "eat_good_food.mp3"))
|
||||
self._eating_bad = pygame.mixer.Sound(path.join(SOUND_PATH, "eat_bad_food.mp3"))
|
||||
self._pass = pygame.mixer.Sound(path.join(SOUND_PATH, "pass.mp3"))
|
||||
self._fail = pygame.mixer.Sound(path.join(SOUND_PATH, "fail.mp3"))
|
||||
self._lv_up = pygame.mixer.Sound(path.join(SOUND_PATH, "lv_up.mp3"))
|
||||
self._lv_down = pygame.mixer.Sound(path.join(SOUND_PATH, "lv_down.mp3"))
|
||||
except Exception:
|
||||
self._is_sound_on = False
|
||||
|
||||
|
@ -43,9 +44,12 @@ class SoundController():
|
|||
self._eating_bad.play()
|
||||
@sound_enabled
|
||||
def play_cheer(self):
|
||||
self._cheer.play()
|
||||
self._pass.play()
|
||||
|
||||
@sound_enabled
|
||||
def play_fail(self):
|
||||
self._fail.play()
|
||||
@sound_enabled
|
||||
def play_lv_up(self):
|
||||
self._lv_up.play()
|
||||
|
||||
|
|