Merge branch 'release/2.2.1-beta'
This commit is contained in:
commit
13c3e825a6
15
README.md
15
README.md
|
@ -22,8 +22,12 @@
|
||||||
```python
|
```python
|
||||||
# main.py
|
# main.py
|
||||||
game = EasyGame(
|
game = EasyGame(
|
||||||
time_to_play, score_to_pass, green_food_count, red_food_count,
|
time_to_play, score_to_pass,
|
||||||
playground_size: list,level: int = -1)
|
green_food_count, red_food_count,
|
||||||
|
playground_size: list,
|
||||||
|
level: int = -1,
|
||||||
|
level_file: str = None,
|
||||||
|
sound: str = "off")
|
||||||
```
|
```
|
||||||
|
|
||||||
- `time_to_play`:遊戲執行的終止時間,單位是 frame,也就是遊戲內部更新畫面的次數,每更新一次 frame +1
|
- `time_to_play`:遊戲執行的終止時間,單位是 frame,也就是遊戲內部更新畫面的次數,每更新一次 frame +1
|
||||||
|
@ -31,7 +35,9 @@ game = EasyGame(
|
||||||
- `red_food_count`:遊戲中紅色食物的數量。
|
- `red_food_count`:遊戲中紅色食物的數量。
|
||||||
- `score_to_pass`:遊戲通關的點數,要超過這個分數才算過關。
|
- `score_to_pass`:遊戲通關的點數,要超過這個分數才算過關。
|
||||||
- `playground_size`:可移動區域的大小。 使用逗號將數字隔開 `width,height` `100,200`
|
- `playground_size`:可移動區域的大小。 使用逗號將數字隔開 `width,height` `100,200`
|
||||||
- `level`: 選定內建關卡,請注意,使用此設定將會覆蓋掉其他設定,預設為 -1 不選擇任何關卡。
|
- `level`: 選定內建關卡,請注意,使用此設定將會覆蓋掉上述其他關卡參數設定,預設為 -1 不選擇任何關卡。
|
||||||
|
- `level_file`: 使用外部檔案作為關卡,請注意,使用此設定將會覆蓋掉關卡編號,並且不會自動進入下一關。
|
||||||
|
- `sound`: 音效。
|
||||||
|
|
||||||
## 玩法
|
## 玩法
|
||||||
|
|
||||||
|
@ -153,7 +159,8 @@ class MLPlay:
|
||||||
{
|
{
|
||||||
"player": "1P",
|
"player": "1P",
|
||||||
"score": 0,
|
"score": 0,
|
||||||
"rank": 1
|
"rank": 1,
|
||||||
|
"passed": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 417 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"game_name": "easy_game",
|
"game_name": "easy_game",
|
||||||
"version": "2.2.0-beta",
|
"version": "2.2.1-beta",
|
||||||
"url": "https://github.com/PAIA-Playful-AI-Arena/easy_game",
|
"url": "https://github.com/PAIA-Playful-AI-Arena/easy_game",
|
||||||
"description": "這是一個吃東西小遊戲,除了讓你熟習所有基本操作,也是 PAIA 的遊戲教學範例",
|
"description": "這是一個吃東西小遊戲,除了讓你熟習所有基本操作,也是 PAIA 的遊戲教學範例",
|
||||||
"logo": [
|
"logo": [
|
||||||
|
@ -82,7 +82,24 @@
|
||||||
"min": -1,
|
"min": -1,
|
||||||
"max": 5,
|
"max": 5,
|
||||||
"default": "-1",
|
"default": "-1",
|
||||||
"help": "選定內建關卡,請注意,使用此設定將會覆蓋掉其他設定,預設為 -1 不選擇任何關卡。"
|
"help": "選定內建關卡,請注意,使用此設定將會覆蓋掉其他關卡設定,預設為 -1 不選擇任何關卡。"
|
||||||
|
}, {
|
||||||
|
"name": "level_file",
|
||||||
|
"verbose": "匯入關卡檔案",
|
||||||
|
"type": "path",
|
||||||
|
"default": "",
|
||||||
|
"help": "可匯入自定義的關卡資料,請參考內建範例的資料格式來設定。使用此設定會覆蓋掉其他關卡設定,也不會使用關卡編號,也不會自動進入下一關。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sound",
|
||||||
|
"verbose": "遊戲音效",
|
||||||
|
"type": "str",
|
||||||
|
"choices": [
|
||||||
|
"on",
|
||||||
|
"off"
|
||||||
|
],
|
||||||
|
"help": "'on' can turn on the sound.",
|
||||||
|
"default": "on"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -3,8 +3,5 @@
|
||||||
"playground_size": [
|
"playground_size": [
|
||||||
500,
|
500,
|
||||||
200
|
200
|
||||||
],
|
]
|
||||||
"score_to_pass": 20,
|
|
||||||
"green_food_count": 3,
|
|
||||||
"black_food_count": 1
|
|
||||||
}
|
}
|
|
@ -1 +1 @@
|
||||||
mlgame>=10.3.1
|
mlgame>=10.3.2
|
|
@ -1,6 +0,0 @@
|
||||||
|
|
||||||
from enum import auto
|
|
||||||
from mlgame.utils.enum import StringEnum
|
|
||||||
class FoodTypeEnum(StringEnum):
|
|
||||||
GREEN = auto()
|
|
||||||
RED = auto()
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
from enum import auto
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
from mlgame.utils.enum import StringEnum
|
||||||
|
# game
|
||||||
|
WIDTH = 800
|
||||||
|
HEIGHT = 600
|
||||||
|
BG_COLOR = "#111111"
|
||||||
|
PG_COLOR = "#B3E5FC"
|
||||||
|
|
||||||
|
# ball
|
||||||
|
BALL_COLOR = "#FFEB3B"
|
||||||
|
BALL_VEL = 10.5
|
||||||
|
BALL_H = 30
|
||||||
|
BALL_W = 30
|
||||||
|
|
||||||
|
|
||||||
|
# food
|
||||||
|
class FoodTypeEnum(StringEnum):
|
||||||
|
GREEN = auto()
|
||||||
|
RED = auto()
|
||||||
|
FOOD_COLOR_MAP = {FoodTypeEnum.GREEN: "#009688",
|
||||||
|
FoodTypeEnum.RED: "#FF1744"}
|
||||||
|
|
||||||
|
# path of assets
|
||||||
|
ASSET_PATH = path.join(path.dirname(__file__), "..", "asset")
|
||||||
|
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")
|
||||||
|
|
||||||
|
|
70
src/game.py
70
src/game.py
|
@ -1,7 +1,6 @@
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
import os.path
|
import os.path
|
||||||
from os import path
|
|
||||||
|
|
||||||
import pygame
|
import pygame
|
||||||
|
|
||||||
|
@ -9,16 +8,10 @@ from mlgame.game.paia_game import PaiaGame, GameResultState, GameStatus
|
||||||
from mlgame.utils.enum import get_ai_name
|
from mlgame.utils.enum import get_ai_name
|
||||||
from mlgame.view.decorator import check_game_progress, check_game_result
|
from mlgame.view.decorator import check_game_progress, check_game_result
|
||||||
from mlgame.view.view_model import *
|
from mlgame.view.view_model import *
|
||||||
from .enums import FoodTypeEnum
|
from .env import *
|
||||||
|
from .env import FoodTypeEnum
|
||||||
from .game_object import Ball, Food
|
from .game_object import Ball, Food
|
||||||
|
from .sound_controller import SoundController
|
||||||
BG_COLOR = "#111111"
|
|
||||||
PG_COLOR = "#B3E5FC"
|
|
||||||
|
|
||||||
WIDTH = 800
|
|
||||||
HEIGHT = 600
|
|
||||||
ASSET_PATH = path.join(path.dirname(__file__), "../asset")
|
|
||||||
LEVEL_PATH = path.join(path.dirname(__file__), "../levels")
|
|
||||||
|
|
||||||
|
|
||||||
class EasyGame(PaiaGame):
|
class EasyGame(PaiaGame):
|
||||||
|
@ -30,18 +23,25 @@ class EasyGame(PaiaGame):
|
||||||
self, time_to_play, score_to_pass, green_food_count, red_food_count,
|
self, time_to_play, score_to_pass, green_food_count, red_food_count,
|
||||||
playground_size: list,
|
playground_size: list,
|
||||||
level: int = -1,
|
level: int = -1,
|
||||||
# level_file,
|
level_file: str = None,
|
||||||
|
sound: str = "off",
|
||||||
*args, **kwargs):
|
*args, **kwargs):
|
||||||
super().__init__(user_num=1)
|
super().__init__(user_num=1)
|
||||||
|
|
||||||
self.game_result_state = GameResultState.FAIL
|
self.game_result_state = GameResultState.FAIL
|
||||||
self.scene = Scene(width=WIDTH, height=HEIGHT, color=BG_COLOR, bias_x=0, bias_y=0)
|
self.scene = Scene(width=WIDTH, height=HEIGHT, color=BG_COLOR, bias_x=0, bias_y=0)
|
||||||
self._level = level
|
self._level = level
|
||||||
if level != -1:
|
self._level_file = level_file
|
||||||
|
if self._level_file is not None or level == "":
|
||||||
self.set_game_params_by_level(level)
|
# set by injected file
|
||||||
|
self.set_game_params_by_file(self._level_file)
|
||||||
|
pass
|
||||||
|
elif self._level != -1:
|
||||||
|
# set by level number
|
||||||
|
self.set_game_params_by_level(self._level)
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
# set by game params
|
||||||
self._playground_w = int(playground_size[0])
|
self._playground_w = int(playground_size[0])
|
||||||
self._playground_h = int(playground_size[1])
|
self._playground_h = int(playground_size[1])
|
||||||
self.playground = pygame.Rect(
|
self.playground = pygame.Rect(
|
||||||
|
@ -56,22 +56,29 @@ class EasyGame(PaiaGame):
|
||||||
self.playground.center = (WIDTH / 2, HEIGHT / 2)
|
self.playground.center = (WIDTH / 2, HEIGHT / 2)
|
||||||
|
|
||||||
self.foods = pygame.sprite.Group()
|
self.foods = pygame.sprite.Group()
|
||||||
|
self.sound_controller = SoundController(sound)
|
||||||
self.init_game()
|
self.init_game()
|
||||||
|
|
||||||
def set_game_params_by_level(self, level):
|
def set_game_params_by_level(self, level):
|
||||||
level_file_path =os.path.join(LEVEL_PATH, f"{level:03d}.json")
|
level_file_path = os.path.join(LEVEL_PATH, f"{level:03d}.json")
|
||||||
if os.path.exists(level_file_path):
|
self.set_game_params_by_file(level_file_path)
|
||||||
# If the file exists, load parameters from the file
|
|
||||||
|
def set_game_params_by_file(self, level_file_path: str):
|
||||||
|
try:
|
||||||
with open(level_file_path) as f:
|
with open(level_file_path) as f:
|
||||||
game_params = json.load(f)
|
game_params = json.load(f)
|
||||||
else:
|
self._set_game_params(game_params)
|
||||||
|
except:
|
||||||
# If the file doesn't exist, use default parameters
|
# 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:
|
with open(os.path.join(LEVEL_PATH, "001.json")) as f:
|
||||||
game_params = json.load(f)
|
game_params = json.load(f)
|
||||||
self._level=1
|
self._level = 1
|
||||||
|
self._level_file=None
|
||||||
|
self._set_game_params(game_params)
|
||||||
|
|
||||||
|
def _set_game_params(self, game_params):
|
||||||
self._playground_w = int(game_params["playground_size"][0])
|
self._playground_w = int(game_params["playground_size"][0])
|
||||||
self._playground_h = int(game_params["playground_size"][1])
|
self._playground_h = int(game_params["playground_size"][1])
|
||||||
self.playground = pygame.Rect(
|
self.playground = pygame.Rect(
|
||||||
|
@ -85,7 +92,6 @@ class EasyGame(PaiaGame):
|
||||||
self._frame_limit = int(game_params["time_to_play"])
|
self._frame_limit = int(game_params["time_to_play"])
|
||||||
self.playground.center = (WIDTH / 2, HEIGHT / 2)
|
self.playground.center = (WIDTH / 2, HEIGHT / 2)
|
||||||
|
|
||||||
|
|
||||||
def init_game(self):
|
def init_game(self):
|
||||||
self.ball = Ball()
|
self.ball = Ball()
|
||||||
self.foods.empty()
|
self.foods.empty()
|
||||||
|
@ -94,6 +100,7 @@ class EasyGame(PaiaGame):
|
||||||
self._create_foods(self._red_food_count, FoodTypeEnum.RED)
|
self._create_foods(self._red_food_count, FoodTypeEnum.RED)
|
||||||
self.frame_count = 0
|
self.frame_count = 0
|
||||||
self._frame_count_down = self._frame_limit
|
self._frame_count_down = self._frame_limit
|
||||||
|
self.sound_controller.play_music()
|
||||||
|
|
||||||
def update(self, commands):
|
def update(self, commands):
|
||||||
# handle command
|
# handle command
|
||||||
|
@ -111,12 +118,15 @@ class EasyGame(PaiaGame):
|
||||||
# handle collision
|
# handle collision
|
||||||
hits = pygame.sprite.spritecollide(self.ball, self.foods, True)
|
hits = pygame.sprite.spritecollide(self.ball, self.foods, True)
|
||||||
if hits:
|
if hits:
|
||||||
|
|
||||||
for food in hits:
|
for food in hits:
|
||||||
if food.type == FoodTypeEnum.GREEN:
|
if food.type == FoodTypeEnum.GREEN:
|
||||||
|
self.sound_controller.play_eating_good()
|
||||||
self.score += 1
|
self.score += 1
|
||||||
self._create_foods(1, FoodTypeEnum.GREEN)
|
self._create_foods(1, FoodTypeEnum.GREEN)
|
||||||
|
|
||||||
elif food.type == FoodTypeEnum.RED:
|
elif food.type == FoodTypeEnum.RED:
|
||||||
|
self.sound_controller.play_eating_bad()
|
||||||
self._create_foods(1, FoodTypeEnum.RED)
|
self._create_foods(1, FoodTypeEnum.RED)
|
||||||
self.score -= 1
|
self.score -= 1
|
||||||
# self._timer = round(time.time() - self._begin_time, 3)
|
# self._timer = round(time.time() - self._begin_time, 3)
|
||||||
|
@ -155,14 +165,20 @@ class EasyGame(PaiaGame):
|
||||||
|
|
||||||
if self.is_running:
|
if self.is_running:
|
||||||
status = GameStatus.GAME_ALIVE
|
status = GameStatus.GAME_ALIVE
|
||||||
elif self.score > self._score_to_pass:
|
elif self.is_passed:
|
||||||
status = GameStatus.GAME_PASS
|
status = GameStatus.GAME_PASS
|
||||||
else:
|
else:
|
||||||
status = GameStatus.GAME_OVER
|
status = GameStatus.GAME_OVER
|
||||||
return status
|
return status
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
if self.score > self._score_to_pass and self._level != -1:
|
|
||||||
|
if self.is_passed:
|
||||||
|
self.sound_controller.play_cheer()
|
||||||
|
|
||||||
|
if self._level_file:
|
||||||
|
pass
|
||||||
|
elif self.is_passed and self._level != -1:
|
||||||
# win and use level will enter next level
|
# win and use level will enter next level
|
||||||
self._level += 1
|
self._level += 1
|
||||||
self.set_game_params_by_level(self._level)
|
self.set_game_params_by_level(self._level)
|
||||||
|
@ -171,6 +187,10 @@ class EasyGame(PaiaGame):
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_passed(self):
|
||||||
|
return self.score > self._score_to_pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_running(self):
|
def is_running(self):
|
||||||
return self.frame_count < self._frame_limit
|
return self.frame_count < self._frame_limit
|
||||||
|
@ -236,7 +256,7 @@ class EasyGame(PaiaGame):
|
||||||
"player": get_ai_name(0),
|
"player": get_ai_name(0),
|
||||||
"rank": 1,
|
"rank": 1,
|
||||||
"score": self.score,
|
"score": self.score,
|
||||||
"passed": self.score > self._score_to_pass
|
"passed": self.is_passed
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,8 @@
|
||||||
import pygame.sprite
|
import pygame.sprite
|
||||||
|
|
||||||
from games.easy_game.src.enums import FoodTypeEnum
|
from games.easy_game.src.env import FoodTypeEnum, FOOD_COLOR_MAP, BALL_COLOR, BALL_VEL, BALL_H, BALL_W
|
||||||
from mlgame.view.view_model import create_rect_view_data
|
from mlgame.view.view_model import create_rect_view_data
|
||||||
|
|
||||||
FOOD_COLOR_MAP = {FoodTypeEnum.GREEN: "#009688",
|
|
||||||
FoodTypeEnum.RED: "#FF1744"}
|
|
||||||
BALL_COLOR = "#FFEB3B"
|
|
||||||
BALL_VEL = 10.5
|
|
||||||
|
|
||||||
BALL_H = 30
|
|
||||||
|
|
||||||
BALL_W = 30
|
|
||||||
|
|
||||||
|
|
||||||
class Ball(pygame.sprite.Sprite):
|
class Ball(pygame.sprite.Sprite):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
from .env import *
|
||||||
|
|
||||||
|
|
||||||
|
def sound_enabled(func):
|
||||||
|
def wrapper(self, *args, **kwargs):
|
||||||
|
if self._is_sound_on:
|
||||||
|
return func(self, *args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class SoundController():
|
||||||
|
def __init__(self, is_sound_on):
|
||||||
|
self._is_sound_on = bool(is_sound_on == "on")
|
||||||
|
if self._is_sound_on:
|
||||||
|
self.load_sounds()
|
||||||
|
|
||||||
|
def load_sounds(self):
|
||||||
|
try:
|
||||||
|
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"))
|
||||||
|
except Exception:
|
||||||
|
self._is_sound_on = False
|
||||||
|
|
||||||
|
@sound_enabled
|
||||||
|
def play_music(self):
|
||||||
|
pygame.mixer.music.play(-1)
|
||||||
|
|
||||||
|
@sound_enabled
|
||||||
|
def play_eating_good(self):
|
||||||
|
self._eating_good.play()
|
||||||
|
|
||||||
|
@sound_enabled
|
||||||
|
def play_eating_bad(self):
|
||||||
|
self._eating_bad.play()
|
||||||
|
@sound_enabled
|
||||||
|
def play_cheer(self):
|
||||||
|
self._cheer.play()
|
Loading…
Reference in New Issue