遊戲技術相關研究

2017年11月17日 星期五

【python】初體驗-俄羅斯方塊遊戲

上午7:44 Posted by Channel Chung No comments

這次還是繼續選用遊戲為主題來學習Python,俄羅斯方塊這個耳熟能詳的遊戲,相信大家多多少少都有玩過,每次學習新程式語言,都會忍不住拿出來致敬一次;以下就開始這次的教程,先來看看完成後的遊戲畫面。。。
接下來先貼出所有程式碼,後面會在針對重點作講解,記的搭配程式碼內的註解一起服用:
[play.py ]
# encoding: utf-8
import os, sys, random
import time
import pygame 
from pygame.locals import *
from drew import *

# 常數-磚塊快速下降速度.
BRICK_DROP_RAPIDLY   = 0.01
# 常數-磚塊正常下降速度.
BRICK_DOWN_SPEED_MAX = 0.5

# 視窗大小.
canvas_width = 800
canvas_height = 600

# 顏色.
color_block         = (0,0,0)
color_white         = (255, 255, 255)
color_red           = (255, 0, 0)
color_gray          = (107,130,114)
color_gray_block    = (20,31,23)
color_gray_green    = (0, 255, 0)

# 定義磚塊.
brick_dict = {
    "10": ( 4, 8, 9,13), "11": ( 9,10,12,13),
    "20": ( 5, 8, 9,12), "21": ( 8, 9,13,14),
    "30": ( 8,12,13,14), "31": ( 4, 5, 8,12), "32": (8,  9, 10, 14), "33": (5,  9, 12, 13),
    "40": (10,12,13,14), "41": ( 4, 8,12,13), "42": (8,  9, 10, 12), "43": (4,  5,  9, 13),
    "50": ( 9,12,13,14), "51": ( 4, 8, 9,12), "52": (8,  9, 10, 13), "53": (5,  8,  9, 13),
    "60": ( 8, 9,12,13),
    "70": (12,13,14,15), "71": ( 1, 5, 9,13)
}

# 方塊陣列(10x20).
bricks_array = []
for i in range(10):
    bricks_array.append([0]*20)
# 方塊陣列(4x4).
bricks = []
for i in range(4):
    bricks.append([0]*4)
# 下一個方塊陣列(4x4).
bricks_next = []
for i in range(4):
    bricks_next.append([0]*4)
# 下一個方塊圖形陣列(4x4).
bricks_next_object = []
for i in range(4):
    bricks_next_object.append([0]*4)    
# 磚塊數量串列.
bricks_list = []
for i in range(10):
    bricks_list.append([0]*20)

# 方塊在容器的位置.
# (-2~6)(  為6的時候不能旋轉方塊).
container_x = 3
# (-3~16)(-3表示在上邊界外慢慢往下掉).
container_y =-4

# 除錯訊息.
debug_message = False
# 判斷遊戲結束.
game_over = False

# 磚塊下降速度.
brick_down_speed = BRICK_DOWN_SPEED_MAX

# 方塊編號(1~7).
brick_id = 1
# 方塊狀態(0~3).
brick_state = 0

# 下一個磚塊編號(1~7).
brick_next_id = 1

# 最大連線數.
lines_number_max = 0
# 本場連線數.
lines_number = 0

# 遊戲狀態.
# 0:遊戲進行中.
# 1:清除磚塊.
game_mode = 0

#-------------------------------------------------------------------------
# 函數:秀字.
# 傳入:
#   text    : 字串.
#   x, y    : 坐標.
#   color   : 顏色.
#-------------------------------------------------------------------------
def showFont( text, x, y, color):
    global canvas    
    text = font.render(text, 1, color) 
    canvas.blit( text, (x,y))

#-------------------------------------------------------------------------
# 函數:取得磚塊索引陣列.
# 傳入:
#   brickId : 方塊編號(1~7).
#   state   : 方塊狀態(0~3).
#-------------------------------------------------------------------------
def getBrickIndex( brickId, state):
    global brick_dict

    # 組合字串.
    brickKey = str(brickId)+str(state)
    # 回傳方塊陣列.
    return brick_dict[brickKey]

#-------------------------------------------------------------------------
# 轉換定義方塊到方塊陣列.
# 傳入:
#   brickId : 方塊編號(1~7).
#   state   : 方塊狀態(0~3).
#-------------------------------------------------------------------------
def transformToBricks( brickId, state):
    global bricks

    # 清除方塊陣列.
    for x in range(4):
        for y in range(4):
            bricks[x][y] = 0
     
    # 取得磚塊索引陣列.
    p_brick = getBrickIndex(brickId, state)
    
    # 轉換方塊到方塊陣列.
    for i in range(4):        
        bx = int(p_brick[i] % 4)
        by = int(p_brick[i] / 4)
        bricks[bx][by] = brickId

    """
    # 印出訊息.
    for y in range(4): 
        s = ""
        for x in range(4): 
            s = s + str(bricks[x][y]) + ","       
        print(s)
    """

#-------------------------------------------------------------------------
# 判斷是否可以複製到容器內.
# 傳出:
#   true    : 可以.
#   false   : 不可以.
#-------------------------------------------------------------------------
def ifCopyToBricksArray():
    global bricks, bricks_array
    global container_x, container_y

    posX = 0
    posY = 0
    for x in range(4):
        for y in range(4):
           if (bricks[x][y] != 0):
                posX = container_x + x
                posY = container_y + y
                if (posX >= 0 and posY >= 0):
                    try:
                        if (bricks_array[posX][posY] != 0):
                            return False
                    except:
                        return False
    return True

#-------------------------------------------------------------------------
# 複製方塊到容器內.
#-------------------------------------------------------------------------
def copyToBricksArray():
    global bricks, bricks_array
    global container_x, container_y
    
    posX = 0
    posY = 0
    for x in range(4):
        for y in range(4):
            if (bricks[x][y] != 0):
                posX = container_x + x
                posY = container_y + y
                if (posX >= 0 and posY >= 0):
                    bricks_array[posX][posY] = bricks[x][y]
     
#-------------------------------------------------------------------------
# 初始遊戲.
#-------------------------------------------------------------------------
def resetGame():
    global BRICK_DOWN_SPEED_MAX
    global bricks_array, bricks, lines_number, lines_number_max

    # 清除磚塊陣列.
    for x in range(10):
        for y in range(20):
            bricks_array[x][y] = 0
            
    # 清除方塊陣列.
    for x in range(4):
        for y in range(4):
            bricks[x][y] = 0

    # 初始磚塊下降速度.
    brick_down_speed = BRICK_DOWN_SPEED_MAX

    # 最大連線數.
    if(lines_number > lines_number_max):
        lines_number_max = lines_number
    # 連線數.
    lines_number = 0

#---------------------------------------------------------------------------
# 判斷與設定要清除的方塊.
# 傳出:
#   連線數
#---------------------------------------------------------------------------
def ifClearBrick():
    pointNum = 0
    lineNum = 0
    for y in range(20):
        for x in range(10):
            if (bricks_array[x][y] > 0):
                pointNum = pointNum + 1
            if (pointNum == 10):
                for i in range(10):
                    lineNum = lineNum + 1
                    bricks_array[i][y] = 9
        pointNum = 0
    return lineNum

#-------------------------------------------------------------------------
# 更新下一個磚塊.
#-------------------------------------------------------------------------
def updateNextBricks(brickId):
    global bricks_next
    
    # 清除方塊陣列.
    for y in range(4):
        for x in range(4):
            bricks_next[x][y] = 0

    # 取得磚塊索引陣列.
    pBrick = getBrickIndex(brickId, 0)

    # 轉換方塊到方塊陣列.
    for i in range(4):
        bx = int(pBrick[i] % 4)
        by = int(pBrick[i] / 4)
        bricks_next[bx][by] = brickId

    # 更新背景區塊.
    background_bricks_next.update()

    # 更新磚塊圖.
    pos_y = 52
    for y in range(4):
        pos_x = 592
        for x in range(4):
            if(bricks_next[x][y] != 0):
                bricks_next_object[x][y].rect[0] = pos_x
                bricks_next_object[x][y].rect[1] = pos_y
                bricks_next_object[x][y].update()
            pos_x = pos_x + 28        
        pos_y = pos_y + 28
                
#-------------------------------------------------------------------------
# 產生新磚塊.
#-------------------------------------------------------------------------
def brickNew():
    global game_over, container_x, container_y, brick_id, brick_next_id, brick_state
    global lines_number, game_mode

    # 判斷遊戲結束.
    game_over = False
    if (container_y < 0):
        game_over = True

    # 複製方塊到容器內.
    container_y = container_y - 1
    copyToBricksArray()  
    
    #------------------------------------------------    
    # 判斷與設定要清除的方塊.
    lines = ifClearBrick() / 10;        
    if (lines > 0):
        # 消除連線數量累加.
        lines_number =  lines_number + lines
        # 修改連線數量.
        #modifyLabel(linesNumber, fontLinesNumber)
        # 1:清除磚塊.
        game_mode = 1

    # 初始方塊位置.
    container_x = 3
    container_y =-4

    # 現在出現方塊.
    brick_id = brick_next_id

    # 下個出現方塊.
    # 方塊編號(1~7).
    brick_next_id = random.randint( 1, 7)
    
    # 初始方塊狀態.
    brick_state = 0

    # GameOver.
    if (game_over):
        # 重新開始遊戲.
        resetGame()
    
#-------------------------------------------------------------------------
# 清除的方塊.
#-------------------------------------------------------------------------
def clearBrick():
    global bricks_array
    # 一列一列判斷清除方塊.
    temp = 0    
    for x in range(10):
        for i in range(19):
            for y in range(20):
                if (bricks_array[x][y] == 9):
                    if (y > 0):
                        temp = bricks_array[x][y - 1]
                        bricks_array[x][y - 1] = bricks_array[x][y]
                        bricks_array[x][y] = temp
                        y = y - 1
            bricks_array[x][0] = 0
#-------------------------------------------------------------------------
# 初始.
pygame.init()
# 顯示Title.
pygame.display.set_caption(u"俄羅斯方塊遊戲")
# 建立畫佈大小.
canvas = pygame.display.set_mode((canvas_width, canvas_height))
# 時脈.
clock = pygame.time.Clock()

# 設定字型-黑體.
font = pygame.font.SysFont('simhei', 26)

# 將繪圖方塊放入陣列.
for y in range(20):
    for x in range(10):
        bricks_list[x][y] = Box(pygame, canvas, "brick_x_" + str(x) + "_y_" + str(y), [ 0, 0, 26, 26], color_gray_block)

# 將繪圖方塊放入陣列.
for y in range(4):
    for x in range(4):
        bricks_next_object[x][y] = Box(pygame, canvas, "brick_next_x_" + str(x) + "_y_" + str(y), [ 0, 0, 26, 26], color_gray_block)

# 背景區塊.
background = Box(pygame, canvas, "background", [ 278, 18, 282, 562], color_gray)

# 背景區塊.
background_bricks_next = Box(pygame, canvas, "background_bricks_next", [ 590, 50, 114, 114], color_gray)

# 方塊編號(1~7).
brick_next_id = random.randint( 1, 7)
# 產生新磚塊.
brickNew()

#-------------------------------------------------------------------------    
# 主迴圈.
#-------------------------------------------------------------------------
running = True
time_temp = time.time()
time_now = 0
while running:
    # 計算時脈.
    time_now = time_now + (time.time() - time_temp)
    time_temp = time.time()
    #---------------------------------------------------------------------
    # 判斷輸入.
    #---------------------------------------------------------------------
    for event in pygame.event.get():
        # 離開遊戲.
        if event.type == pygame.QUIT:
            running = False        
        # 判斷按下按鈕
        if event.type == pygame.KEYDOWN:
            #-----------------------------------------------------------------
            # 判斷按下ESC按鈕
            if event.key == pygame.K_ESCAPE:
                running = False
            # 除錯訊息開關.
            elif event.key == pygame.K_d:
                debug_message = not debug_message                
            #-----------------------------------------------------------------
            # 變換方塊-上.
            elif event.key == pygame.K_UP and game_mode == 0:
                # 在右邊界不能旋轉.
                if (container_x == 8):
                    break
                # 判斷磚塊N1、N2、I.
                if (brick_id == 1 or brick_id == 2 or brick_id == 7):
                    # 長條方塊旋轉例外處理.
                    if (brick_id == 7):
                        if (container_x < 0 or container_x == 7):
                            break
                    # 旋轉方塊.
                    brick_state = brick_state + 1
                    if (brick_state > 1):
                        brick_state = 0                    
                    # 轉換定義方塊到方塊陣列.
                    transformToBricks(brick_id, brick_state)
                    # 碰到磚塊.
                    if (not ifCopyToBricksArray()):
                        brick_state = brick_state - 1
                        if (brick_state < 0):
                            brick_state = 1
                # 判斷磚跨L1、L2、T.                                
                elif (brick_id == 3 or brick_id == 4 or brick_id == 5):
                    # 旋轉方塊.
                    brick_state = brick_state + 1
                    if (brick_state > 3):
                        brick_state = 0                    
                    # 轉換定義方塊到方塊陣列.
                    transformToBricks(brick_id, brick_state)
                    # 碰到磚塊.
                    if (not ifCopyToBricksArray()):
                        brick_state = brick_state - 1
                        if (brick_state < 0):
                            brick_state = 3
            #-----------------------------------------------------------------
            # 快速下降-下.
            elif event.key == pygame.K_DOWN and game_mode == 0:
                # 磚塊快速下降.
                brick_down_speed = BRICK_DROP_RAPIDLY
            #-----------------------------------------------------------------
            # 移動方塊-左.
            elif event.key == pygame.K_LEFT and game_mode == 0:
                container_x = container_x - 1
                if (container_x < 0):
                    if (container_x == -1):
                        if (bricks[0][0] != 0 or bricks[0][1] != 0 or bricks[0][2] != 0 or bricks[0][3] != 0):
                            container_x = container_x + 1
                    elif (container_x == -2): 
                        if (bricks[1][0] != 0 or bricks[1][1] != 0 or bricks[1][2] != 0 or bricks[1][3] != 0):
                            container_x = container_x + 1
                    else:
                        container_x = container_x + 1
                # 碰到磚塊.
                if (not ifCopyToBricksArray()):
                    container_x = container_x + 1
            #-----------------------------------------------------------------
            # 移動方塊-右.
            elif event.key == pygame.K_RIGHT and game_mode == 0:
                container_x = container_x + 1
                if (container_x > 6):
                    if (container_x == 7):
                        if (bricks[3][0] != 0 or bricks[3][1] != 0 or bricks[3][2] != 0 or bricks[3][3] != 0):
                            container_x = container_x - 1;                        
                    elif (container_x == 8):
                        if (bricks[2][0] != 0 or bricks[2][1] != 0 or bricks[2][2] != 0 or bricks[2][3] != 0):
                            container_x = container_x - 1                        
                    else:
                        container_x = container_x - 1
                # 碰到磚塊.
                if (not ifCopyToBricksArray()):
                    container_x = container_x - 1                    
        #-----------------------------------------------------------------
        # 判斷放開按鈕
        if event.type == pygame.KEYUP:
            # 快速下降-下.
            if event.key == pygame.K_DOWN:
                # 恢復正常下降速度.
                brick_down_speed = BRICK_DOWN_SPEED_MAX
        
    #---------------------------------------------------------------------    
    # 清除畫面.
    canvas.fill(color_block)

    # 遊戲中.
    if (game_mode == 0):
        # 處理磚塊下降.
        if(time_now >= brick_down_speed):
            # 往下降.
            container_y = container_y + 1; 
            # 碰到磚塊.
            if (not ifCopyToBricksArray()):
                #產生新塊.
                brickNew()            
            # 轉換定義方塊到方塊陣列(bricks).
            transformToBricks( brick_id, brick_state)
            # 清除時脈.
            time_now = 0
    # 清除磚塊.
    elif (game_mode == 1):
        # 清除的方塊.
        clearBrick()
        # 遊戲中.
        game_mode = 0
        # 轉換定義方塊到方塊陣列.
        transformToBricks(brick_id, brick_state)

    #---------------------------------------------------------------------    
    # 更新下一個磚塊圖形.
    updateNextBricks(brick_next_id)
    # 更新繪圖.
    pos_y = 20
    # 更新背景區塊.
    background.update()
    for y in range(20):
        pos_x = 280
        for x in range(10):
            if(bricks_array[x][y] != 0):
                bricks_list[x][y].rect[0] = pos_x
                bricks_list[x][y].rect[1] = pos_y
                bricks_list[x][y].update()
            pos_x = pos_x + 28        
        pos_y = pos_y + 28    
    # 更新方塊
    for y in range(4):
        for x in range(4):            
            if (bricks[x][y] != 0):
                posX = container_x + x
                posY = container_y + y
                if (posX >= 0 and posY >= 0):
                    bricks_list[posX][posY].rect[0] = (posX * 28) + 280
                    bricks_list[posX][posY].rect[1] = (posY * 28) + 20
                    bricks_list[posX][posY].update()
    #---------------------------------------------------------------------    
    # 除錯訊息.
    if(debug_message):
        # 更新容器.
        str_x = ""
        pos_x = 15
        pos_y = 20
        for y in range(20):
            str_x = ""
            for x in range(10):
                str_x = str_x + str(bricks_array[x][y]) + " "
            showFont( str_x, pos_x, pos_y, color_red)
            pos_y = pos_y + 28
            
        # 更新方塊
        posX = 0
        posY = 0    
        for y in range(4):
            str_x = ""
            for x in range(4):            
                if (bricks[x][y] != 0):
                    posX = container_x + x
                    posY = container_y + y
                    if (posX >= 0 and posY >= 0):
                        str_x = str_x + str(bricks[x][y]) + " "
                else:
                    str_x = str_x + "  "
            pos_x = 15 + (container_x * 26)
            pos_y = 20 + (posY * 28)
            showFont( str_x, pos_x, pos_y, color_white)

    # 顯示訊息.
    showFont( u"下次出現方塊", 588, 16, color_gray)

    showFont( u"最大連線數", 588, 190, color_gray)
    showFont( str(int(lines_number_max)), 588, 220, color_gray)

    showFont( u"本局連線數", 588, 260, color_gray)
    showFont( str(int(lines_number)), 588, 290, color_gray)

    # 顯示FPS.
    # 除錯訊息.
    if(debug_message):    
        showFont( u"FPS:" + str(clock.get_fps()), 6, 0, color_gray_green)    

    # 更新畫面.
    pygame.display.update()
    clock.tick(60)

# 離開遊戲.
pygame.quit()
quit() 
25~34行:這邊定義了所有方塊的編碼:
從上面一系列圖式不難觀察出方塊的編碼是以"方塊代號":(方塊格子編號,...)的方式作編碼。
其他部分想信只要詳細看過程式碼內的註解就不難了解程式的運作原理。
[drew.py ]
# encoding: utf-8

#-------------------------------------------------------------------------
# 畫Box.
#-------------------------------------------------------------------------
class Box(object):
    #-------------------------------------------------------------------------
    # 建構式.
    #   pygame    : pygame.
    #   canvas    : 畫佈.
    #   name    : 物件名稱.
    #   rect      : 位置、大小.
    #   color     : 顏色.
    #-------------------------------------------------------------------------
    def __init__( self, pygame, canvas, name, rect, color):
        self.pygame = pygame
        self.canvas = canvas
        self.name = name
        self.rect = rect
        self.color = color

        self.visivle = True
        
    #-------------------------------------------------------------------------
    # 更新.
    #-------------------------------------------------------------------------
    def update(self):
        if(self.visivle):
            self.pygame.draw.rect( self.canvas, self.color, self.rect)
drew.py主要是處理畫方塊

最後請在在命令列輸入以下指令以執行俄羅斯方塊遊戲:

 python play.py

操作方式:
Esc鍵:離開遊戲
D 鍵:除錯訊息開關
左右:移動方塊
上:旋轉方塊
下:方塊快速往下移動

以下是試玩影片

有興趣的同學一樣可以到GitHub下載程式碼來研究

點我到GitHub下載範例程式

2017年10月15日 星期日

【python】初體驗-打磚塊遊戲

上午8:07 Posted by Channel Chung 4 comments
Python。。是的,這次要來玩的就是Python了,礙於每天看一些程式技術網站這個語言出現率非很高,所以就新起了一探究竟的慾望,廢話不多說,直接至Python官網下載安裝包,一路Next後安裝完成,接下開始填寫第一支測試程式Hello World。。。

>>> print ("Hello World!!")


打完收工。。。喂!!給我認真一點啦!!

好啦,那就依照慣例來個打磚塊遊戲好了,google了一下發現大家在Python上寫2D Game幾乎都是用pygame套件,那我們一樣也用這套件來寫這次的主角打磚塊遊戲好了,首先用pip安裝pygame套件,請在命令列輸入以下指令:

pip install pygame 

安裝完畢後就可以開啟您習慣的編輯器鍵入程式碼:
將以下程式碼存成play.py檔名
# encoding: utf-8

import os, sys, random
import pygame 
from pygame.locals import *

from drew import *

# 視窗大小.
canvas_width = 800
canvas_height = 600

# 顏色.
block = (0,0,0)

# 磚塊數量串列.
bricks_list = []

# 移動速度.
dx =  8
dy = -8

# 遊戲狀態.
# 0:等待開球
# 1:遊戲進行中
game_mode = 0

#-------------------------------------------------------------------------
# 函數:秀字.
#-------------------------------------------------------------------------
def showFont( text, x, y):
    global canvas    
    text = font.render(text, 1, (255, 0, 0)) 
    canvas.blit( text, (x,y))

#-------------------------------------------------------------------------
# 函數:碰撞判斷.
#   x       : x 
#   y       : y 
#   boxRect : 矩形
#-------------------------------------------------------------------------
def isCollision( x, y, boxRect):
    if (x >= boxRect[0] and x <= boxRect[0] + boxRect[2] and y >= boxRect[1] and y <= boxRect[1] + boxRect[3]):
        return True;          
    return False;  

#-------------------------------------------------------------------------
# 函數:初始遊戲.
#-------------------------------------------------------------------------
def resetGame():
    # 宣告使用全域變數.
    global game_mode, brick_num, bricks_list, dx, dy

    # 磚塊
    for bricks in bricks_list:
        # 亂數磚塊顏色
        r = random.randint(100,200)
        g = random.randint(100,200)
        b = random.randint(100,200)
        bricks.color = [r,g,b]        
        # 開啟磚塊.
        bricks.visivle = True
    # 0:等待開球
    game_mode = 0
    # 磚塊數量.
    brick_num = 99    
    # 移動速度.
    dx =  8
    dy = -8

# 初始.
pygame.init()
# 顯示Title.
pygame.display.set_caption(u"打磚塊遊戲")
# 建立畫佈大小.
canvas = pygame.display.set_mode((canvas_width, canvas_height))
# 時脈.
clock = pygame.time.Clock()

# 設定字型-黑體.
font = pygame.font.SysFont('simhei', 18)

# 底板.
paddle_x = 0
paddle_y = (canvas_height - 48)
paddle = Box(pygame, canvas, "paddle", [paddle_x, paddle_y, 100, 24], (255,255,255))

# 球.
ball_x = paddle_x
ball_y = paddle_y
ball   = Circle(pygame, canvas, "ball", [ball_x, ball_x], 8, (255,255,255))

# 建立磚塊
brick_num = 0
brick_x = 70
brick_y = 60
brick_w = 0
brick_h = 0
for i in range( 0, 99):
    if((i % 11)==0):
        brick_w = 0
        brick_h = brick_h + 18        
    bricks_list.append (Box(pygame, canvas, "brick_"+str(i), [  brick_w + brick_x, brick_h+ brick_y, 58, 16], [255,255,255]))
    brick_w = brick_w + 60
# 初始遊戲.
resetGame()

#-------------------------------------------------------------------------    
# 主迴圈.
#-------------------------------------------------------------------------
running = True
while running:
    #---------------------------------------------------------------------
    # 判斷輸入.
    #---------------------------------------------------------------------
    for event in pygame.event.get():
        # 離開遊戲.
        if event.type == pygame.QUIT:
            running = False
        # 判斷按下按鈕
        if event.type == pygame.KEYDOWN:
            # 判斷按下ESC按鈕
            if event.key == pygame.K_ESCAPE:
                running = False
                
        # 判斷Mouse.
        if event.type == pygame.MOUSEMOTION:
            paddle_x = pygame.mouse.get_pos()[0] - 50
        if event.type == pygame.MOUSEBUTTONDOWN:
            if(game_mode == 0):
                game_mode = 1

    #---------------------------------------------------------------------    
    # 清除畫面.
    canvas.fill(block)
    
    # 磚塊
    for bricks in bricks_list:
        # 球碰磚塊.
        if(isCollision( ball.pos[0], ball.pos[1], bricks.rect)):
            if(bricks.visivle):                
                # 扣除磚塊.
                brick_num = brick_num -1
                # 初始遊戲.
                if(brick_num <= 0):
                    resetGame()
                    break
                # 球反彈.
                dy = -dy; 
            # 關閉磚塊.
            bricks.visivle = False

        # 更新磚塊.        
        bricks.update()
            
    #顯示磚塊數量.
    showFont( u"磚塊數量:"+str(brick_num),   8, 20)            

    # 秀板子.
    paddle.rect[0] = paddle_x
    paddle.update()

    # 碰撞判斷-球碰板子.
    if(isCollision( ball.pos[0], ball.pos[1], paddle.rect)):        
        # 球反彈.
        dy = -dy;         
            
    # 球.
    # 0:等待開球
    if(game_mode == 0):
        ball.pos[0] = ball_x = paddle.rect[0] + ( (paddle.rect[2] - ball.radius) >> 1 )
        ball.pos[1] = ball_y = paddle.rect[1] - ball.radius        
    # 1:遊戲進行中
    elif(game_mode == 1):
        ball_x += dx
        ball_y += dy
        #判斷死亡.
        if(ball_y + dy > canvas_height - ball.radius):
            game_mode = 0        
        # 右牆或左牆碰撞.
        if(ball_x + dx > canvas_width - ball.radius or ball_x + dx < ball.radius):
            dx = -dx
        # 下牆或上牆碰撞
        if(ball_y + dy > canvas_height - ball.radius or ball_y + dy < ball.radius):        
            dy = -dy
        ball.pos[0] = ball_x
        ball.pos[1] = ball_y

    # 更新球.
    ball.update()

    # 顯示中文.
    showFont( u"FPS:" + str(clock.get_fps()), 8, 2)    
    # 更新畫面.
    pygame.display.update()
    clock.tick(60)

# 離開遊戲.
pygame.quit()
quit()

將以下程式碼存成drew.py檔名
# encoding: utf-8

#-------------------------------------------------------------------------
# 畫Box.
#-------------------------------------------------------------------------
class Box(object):
    #-------------------------------------------------------------------------
    # 建構式.
    #   pygame    : pygame.
    #   canvas    : 畫佈.
    #   name    : 物件名稱.
    #   rect      : 位置、大小.
    #   color     : 顏色.
    #-------------------------------------------------------------------------
    def __init__( self, pygame, canvas, name, rect, color):
        self.pygame = pygame
        self.canvas = canvas
        self.name = name
        self.rect = rect
        self.color = color

        self.visivle = True
        
    #-------------------------------------------------------------------------
    # 更新.
    #-------------------------------------------------------------------------
    def update(self):
        if(self.visivle):
            self.pygame.draw.rect( self.canvas, self.color, self.rect)

#-------------------------------------------------------------------------
# 畫圓.
#-------------------------------------------------------------------------
class Circle(object):
    #-------------------------------------------------------------------------
    # 建構式.
    #   pygame  : pygame.
    #   canvas  : 畫佈.
    #   name    : 物件名稱.
    #   pos     : 位置.  
    #   radius  : 大小.
    #   color   : 顏色.    
    #-------------------------------------------------------------------------
    def __init__( self, pygame, canvas, name, pos, radius, color):
        self.pygame = pygame
        self.canvas = canvas
        self.name = name
        self.pos = pos
        self.radius = radius
        self.color = color
        
        self.visivle = True

    #-------------------------------------------------------------------------
    # 更新.
    #-------------------------------------------------------------------------
    def update(self):
        if(self.visivle):
            self.pygame.draw.circle( self.canvas, self.color, self.pos , self.radius)

在命令列輸入以下指令以執行打磚塊遊戲:

python play.py

以下是試玩影片

運作原理就不多說了,有興趣的同學可以到GitHub下載程式碼來研究,程式碼裡面寫了大量的註解與說明,相信不難理解,那就先這樣咱們下次見囉。

點我到GitHub下載範例程式

-2017/10/23 更新
[新增經典灰階美術風格]
在命令列輸入以下指令以執行遊戲:

python play_old_school.py


















2017年8月9日 星期三

【node.js】自製小型網路引擎-打磚塊連線(2)

上午8:01 Posted by Channel Chung No comments


繼續上一篇的主題,只是這次我們將打磚塊遊戲改寫成可以同時看到其他玩家玩打磚塊的畫面,如下圖所示,中間為玩家可以操作的部分,左右兩邊可以看到其他玩家玩打磚塊的過程。

(以上畫面是開了三個Chrome瀏覽器來模擬三個玩家)

以下是實際運作的影片:

 

因為使用socket.io套件關係所以我們的打磚塊遊戲也可以在手機上的瀏覽器運作,並且可以與PC瀏覽器內的打磚塊遊戲連線,影片如下:



經過這兩個打磚塊遊戲的連線練習後對Node.js的socket.io套件也已經有了初步的了解,接下來會在抽空試試使用net套件來跟Unity作溝通。

2017年7月30日 星期日

【node.js】自製小型網路引擎-打磚塊連線(1)

上午12:20 Posted by Channel Chung No comments

是的,又開始手癢想要玩些新東西了,所以趁著我們家遊戲上線這段小小的空檔趕快來稿一下,這次要玩的是好久沒碰的網路引擎,剛好又一直想要找時間摸摸node.js,那就順水推舟這次Server端語言就用node.js,Client端就用之前使用three.js寫的打磚塊來作連線的練習,大至上想要達成的目標如下:


上圖中間是玩家操作的打磚塊,左右兩邊的打磚塊是準備接受Server的廣播後同步玩家操作的狀態;在玩家操作部份我們這次只會同步球的X、Y座標跟板子的X座標,Server接收到訊息後會立刻廣播球跟板子的座標給左右兩邊的打磚塊遊戲,這樣看左右兩邊的打磚塊運作就會跟玩家操作的打磚塊畫面一樣了,至於左右兩邊碰撞到磚塊會不見的原理是我們同步玩家球的位置,所以左右兩邊在球碰撞到磚塊的時候程式就會自動執行讓磚塊消失的邏輯,以下是這次實作的影片:



影片最後會發現我們我們將Server程式關閉後,左右兩邊的打磚塊就不動了,主要是因為玩家操作的打磚塊訊息已經無法透過Server將資料同步給左右兩邊的打磚塊,所以左右兩邊的打磚塊就停止運作了。

其實只同步球跟板子的作法並不完美,一來需要頻繁的傳送同步訊息,二來只要網路延遲就有可能導致左右兩邊的磚塊數量不同步,因為球有可能會穿透或在未碰到磚塊時接收到反彈球訊息等等狀況導致,至於解決方法也不難,就是拿掉左右兩邊打磚塊遊戲的磚塊碰撞判斷然後由中間玩家發出碰到那些磚塊訊息給左右兩端的打

 (右邊的打磚塊已經出現不同步的狀況)

磚塊遊戲就可以解決磚塊不同步問題了,這次的練習就先到這裡,下次會繼續實作讓三個不同玩家,同時連線在畫面上的三個不同區域玩打磚塊遊戲,咱們下次見囉。

2017年5月13日 星期六

【three.js】最強WebGL封裝套件-致敬經典俄羅斯方塊

上午2:01 Posted by Channel Chung No comments

點我玩遊戲

操作說明:
   上 or W:旋轉方塊
   下 or S :控制方塊快速下降
   左 or A :左移動方塊
   右 or D :右移動方塊

遊戲介紹:
    今天就繼續以個人最愛的經典遊戲來練習three.js,俄羅斯方塊大家因該多多少少都有看過或玩過吧,這也是今天的練習主題,廢話就不多說先上遊戲畫面圖:


下面是是遊戲影片:


另外這次程式碼有檔手持裝置,所以使用手持裝置開啟網址時會跳出警告視窗,其實three.js也是支援手持裝置的,只是我還沒處理觸碰螢幕的操作輸入,所以就暫時先過濾掉手持裝置了,等哪天想到再來加觸碰螢幕的方塊操作功能。

有興趣的同學也一樣可以到GitHub下載原始碼,只限用於學術研究用,請勿使用在商業用途上。
點我至GitHub下載原始碼


-v0.02 版本更新
[新增設定視窗]
  •   cameraZoom:縮放攝影機
    •  speed:磚塊下降速度
    •  AI:AI開啟狀態
      • 無:不使用AI
      • mode 1:使用第一種AI
        • 參考網址:
      • mode 2:使用第二種AI
        • 參考網址:
    • Close Controls:關閉設定視窗
    •  Open Controls:開啟設定視窗
 -影片如下:

 












 

2017年4月30日 星期日

【three.js】最強WebGL封裝套件-致敬經典打磚塊

上午4:41 Posted by Channel Chung No comments
 
點我玩遊戲

操作說明:
    滑鼠左鍵:開球
    滑鼠游標:版子跟隨滑鼠游標左右移動

遊戲介紹:
    每位遊戲設計人員心中都有幾款影響自己深遠的經典遊戲,對於我來說紅白機快打磚塊這款遊戲在我心中就是名列前茅的少數難忘經典遊戲,以至於在往後的人生中只要接觸新的程式語言我都會毫不猶豫的在把這款心中的經典在"致敬"一次,此次初嘗three.js在一邊摸索下也簡單的把打磚塊雛形實作了出來,遊戲一共有8個關卡  如下:




 同時支援PC與Mobile的瀏覽器:


有興趣的同學也可以到GitHub下載原始碼,不過只限用於學術研究用,請勿使用在商業用途上。
點我至GitHub下載原始碼

另外如果您也喜歡打磚塊遊戲的話TAITO公司在IOS上也已經推出了重製版的ARKANOID遊戲,下載網址如下:
https://itunes.apple.com/us/app/arkanoid/id328321410?mt=8

-v0.02 版本更新
[新增設定視窗]
  • 設定視窗內容說明:
    • cameraZoom:縮放攝影機
    • AI:開關AI
    • Close Controls:關閉設定視窗
    •  Open Controls:開啟設定視窗
  -影片如下:

2017年3月20日 星期一

【three.js】最強WebGL封裝套件-初體驗

上午10:09 Posted by Channel Chung 4 comments
  這一年多來因為工作的關係主要的開發工具機乎都是使用Unity,某天想到Unity可以Build出webGL檔案在瀏覽器上執行所以就小試了一下,不試還好一試嚇一跳,空專案編譯出來的檔案居然高達4MB多,想說因該是哪裡搞錯了所以就跑去問了Google大神,大神很誠懇的對著我說孩子看著我的眼睛聽我說,先不要管Unity編譯webGL了,你聽說過three.js嗎?我先給你一些three.js正妹的個人資料並盡快安排你們碰個面聊聊。。。聽到對方是正妹原本已經發聲到喉嚨的拒絕聲讓我硬是又把它吞了回去。。
記的碰面的那天外面下著小雨,我們靦腆的坐在咖啡廳靠窗的位置,在我開口跟妳要了LINE後雙方就一直低著頭滑著自己的手機,忽然間我的LINE收到了妳的一則訊息還有一行網址:

Hi 我們先來作個心理測試,你先點選下面網址確定您的瀏覽器有沒有支援webGL,如果有支援的話我就可以告訴你更多關於我的事情。。。
https://get.webgl.org/

看完後我二話不說的點了連接,瀏覽器畫面上顯示了大大的"Your browser supports WebGL"字樣,我開心極了,這表示我們可能有機會更進一步了解彼此,我快速的在LINE上敲入了幾個字:

我。。。今天的穿著還可以吧? 妳平常出門都打扮怎麼亮眼嗎?
(Normal Mapping+SpotLight) [點擊另開視窗看效果]
(貼圖引用自https://samsilcox.wordpress.com/2015/11/12/normal-and-specular-maps/)

妳收到訊息後抬頭看我了一眼又快速的低下頭打起了簡訊。。。

你今天的打扮很帥氣。。。
至於我,習慣看場合穿著,比如說在家我會穿的比較普素一點比如這樣:
(MeshBasicMaterial) [點擊另開視窗看效果]

跟朋友外出的話我會穿著比較亮麗一點比如這樣:
(MeshPhysicalMaterial+SpotLight) [點擊另開視窗看效果]

如果是重要場合的話我穿著會比較隆重就像這樣:
(Bump Map+SpotLight) [點擊另開視窗看效果]

另外,睡覺的時候我不習慣穿衣服
(MeshNormalMaterial) [點擊另開視窗看效果]
看著手機裡妳不斷傳送過來的訊息,除了讓我對妳的穿著品味有一定的了解外,更驚訝的發現妳的敘述簡潔易懂,讓我開始在腦海裡幻想起未來我們可以一起完成許多事情,第六感告訴我在這樣下去的話我很可能會狠狠的喜歡上妳;這之後我們又聊了許多事情,直到店家服務生過來對我們說今天營業時間已經結束囉,我們才依依不捨的離開,離開前也約定好了下次碰面的時間,這讓我開始期待下次的約會了。。。

後記:為了紀念我們的第一次約會我將今天的所有聊天內容永久的紀錄在github上了