從零開始寫一個武俠冒險游戲-3-地圖生成


從零開始寫一個武俠冒險游戲-3-地圖生成

  • 作者:FreeBlues
  • 修訂記錄
    • 2016.06.13 初稿完成.
    • 2016.08.03 增加對 XCode 項目文件的說明.

概述

前面兩章我們設計了角色的狀態, 繪制出了角色, 並且賦予角色動作, 現在是時候為我們的角色創造一個舞台了, 那就是游戲地圖(我們目前做的是一個2D 游戲, 因此叫地圖, 如果是 3D, 則叫地形).

地圖生成也是游戲開發的一項基本技術, 涉及到方方面面的技能, 而且地圖的數據結構要考慮到游戲里的其他景物跟角色的顯示和交互, 對於整個游戲程序的效率起着決定性的影響, 不過我們這里先解決有沒有的問題, 目標不太高, 能流暢運行就可以了.

最簡原型

跟我們一向提倡的大思路一致, 一切從簡出發, 先弄個原型跑起來再說.

經驗之談: 很多開發過程中的難題都是因為我們一開始就引入了過於復雜的問題, 制定了太大的目標, 試圖一開始就把方方面面都考慮到, 結果無形中就增加了難度, 不得不承認, 這種頂層設計的思路是不太符合事物發展的規律的, 也不符合生物的進化規律, 所以實現起來就比較困難, 如果我們遵循從簡單到復雜, 從原型到成品的開發思路, 就會發現開發過程變得順利很多.

游戲地圖原理

簡單說來, 游戲地圖有兩個層面, 一個是顯示到屏幕上的圖形圖像, 一個是隱藏在圖像后面的數據結構, 前者是游戲跟玩家交互的界面, 后者是游戲中繪制出來的各種對象跟程序交互的接口.

比如玩家操縱一個游戲角色從左邊一個位置走到右邊一個位置, 玩家看到的是屏幕上角色的移動過程, 而程序在后面要記錄玩家每時每刻的坐標, 以及該坐標在地圖上對應的位置.

如果玩家看到地圖上某個位置有一個可以操作的物體, 比如一個箱子, 玩家的角色想要靠近這個箱子然后打開它, 那么后台的地圖數據庫里首先要在地圖的某個位置上有一個箱子, 然后再判斷角色距離箱子的距離, 如果小於某個值, 那么就說明允許操作, 玩家開過箱子后, 還要把箱子的當前狀態(已開啟)再寫回到數據庫里, 等等諸如此類.

最簡單的地圖

最簡單的地圖就是一張事先畫好的圖, 角色在這張圖上移來移去, 這個功能我們在第2章就已經實現了, 但是按照這種方法實現的地圖角色很難跟地圖上的物體進行交互, 而且使用事先畫好的圖做地圖還有一個問題就是如果整個游戲場景比較大的話就需要很多畫預先存儲到游戲中, 這樣會導致較大的體積.

所以, 我們采取另一種做法, 因為游戲場景中很多物體對象都是可以重復使用的, 比如樹木, 岩石等等, 所以我們可以把這些基本對象提取出來事先繪制好, 或者使用預先做好的素材, 這樣我們需要事先存儲的內容就大大減少了, 然后再根據實際需要動態繪制上去, 這就是隨機生成場景地圖的做法.

恰好我之前寫過一個簡單的隨機地圖生成器, 雖然比較簡陋, 不過為了減少工作量, 還是可以拿來用用的, 當然, 直接用是不行的, 主要是以它做一個基礎來進行改寫.

原型目標

首先明確一下我們這個地圖原型的基本需求點:

  • 可以靈活調整地圖大小
  • 可以隨機插入樹木/礦物/建築等固定物體
  • 角色可以跟地圖上的這些物體交互

這是三個最基本的需求, 我們一步一步來實現這三個需求.

格子地面地圖

綜合性能和實現難度方面的考慮, 我們的地圖以網格的形式進行繪制和保存, 也就是以我們之前寫好的那個隨機地圖生成器為基本原型, 這樣一方面可以靈活控制數據表的大小, 數據表中存儲的最小單位就是一個預先設定好大小的格子, 另一方面寫起來也比較簡單, 還有不錯的效率表現.

首先確定我們的初始化參數和數據結構, 用這個函數來實現:

function initParams()
    print("Simple Map Sample!!")
    textMode(CORNER)
    spriteMode(CORNER)
    
    --[[
    gridCount:網格數目,范圍:1~100,例如,設為3則生成3*3的地圖,設為100,則生成100*100的地圖。
    scaleX:單位網格大小比例,范圍:1~100,該值越小,則單位網格越小;該值越大,則單位網格越大。
    scaleY:同上,若與scaleX相同則單位網格是正方形格子。
    plantSeed:植物生成幾率,范圍:大於4的數,該值越小,生成的植物越多;該值越大,生成的植物越少。
    minerialSeed:礦物生成幾率,范圍:大於3的數,該值越小,生成的礦物越多;該值越大,生成的礦物越少。
    --]]
    gridCount = 50
    scaleX = 50
    scaleY = 50
    plantSeed = 20.0
    minerialSeed = 50.0
    
    -- 根據地圖大小申請圖像
    local w,h = (gridCount+1)*scaleX, (gridCount+1)*scaleY
    imgMap = image(w,h)
        
    -- 整個地圖使用的全局數據表
    mapTable = {}
    
    -- 設置物體名稱
    tree1,tree2,tree3 = "松樹", "楊樹", "小草"    
    mine1,mine2 = "鐵礦", "銅礦"
        
    -- 設置物體圖像
    imgTree1 = readImage("Planet Cute:Tree Short")
    imgTree2 = readImage("Planet Cute:Tree Tall")
    imgTree3 = readImage("Platformer Art:Grass")
    imgMine1 = readImage("Platformer Art:Mushroom")
    imgMine2 = readImage("Small World:Treasure")
    
    -- 存放物體: 名稱,圖像
    itemTable = {[tree1]=imgTree1,[tree2]=imgTree2,[tree3]=imgTree3,[mine1]=imgMine1,[mine2]=imgMine2}
       
    -- 3*3 
    mapTable = {{pos=vec2(1,1),plant=nil,mineral=mine1},{pos=vec2(1,2),plant=nil,mineral=nil},
                {pos=vec2(1,3),plant=tree3,mineral=nil},{pos=vec2(2,1),plant=tree1,mineral=nil},
                {pos=vec2(2,2),plant=tree2,mineral=mine2},{pos=vec2(2,3),plant=nil,mineral=nil},
                {pos=vec2(3,1),plant=nil,mineral=nil},{pos=vec2(3,2),plant=nil,mineral=mine2},
                {pos=vec2(3,3),plant=tree3,mineral=nil}}
    
end

接下來是繪制地面單位格子的函數, 現在是在每個格子上繪制一個矩形, 參數 position 是一個二維向量, 形如 vec(1,2) 則表示該格子位於第1行, 第2列, 代碼如下:

-- 繪制單位格子地面
function drawUnitGround(position)
    local x,y = scaleX * position.x, scaleY * position.y
    pushMatrix()
    stroke(99, 94, 94, 255)
    -- 網格線寬度
    strokeWidth(1)
    -- 地面顏色
    fill(5,155,40,255)
    -- fill(5,155,240,255)
    rect(x,y,scaleX,scaleY)
    popMatrix()
end

用這兩個函數來調用它:

-- 新建地圖數據表, 插入地圖上每個格子里的物體數據
function createMapTable()
    for i=1,gridCount,1 do
        for j=1,gridCount,1 do
            mapItem = {pos=vec2(i,j), plant=nil, mineral=nil}
            table.insert(mapTable, mapItem)
        end
    end
    updateMap()
end

-- 更新地圖
function updateMap()
    setContext(imgMap)   
    for i = 1,gridCount*gridCount,1 do
        local pos = mapTable[i].pos
        -- 繪制地面
        drawUnitGround(pos)
    end
    setContext()
end

-- 繪制地圖
function drawMap() 
    -- 繪制地圖
    sprite(imgMap,-scaleX,-scaleY)
end

最基本原型的完整代碼

下面我們把實現這個最基本原型的完整代碼列出來:

-- MapSample

-- 初始化地圖參數
function initParams()
    print("地圖初始化開始...")
    textMode(CORNER)
    spriteMode(CORNER)
    
    --[[ 參數說明:
    gridCount:網格數目,范圍:1~100,例如,設為3則生成3*3的地圖,設為100,則生成100*100的地圖。
    scaleX:單位網格大小比例,范圍:1~100,該值越小,則單位網格越小;該值越大,則單位網格越大。
    scaleY:同上,若與scaleX相同則單位網格是正方形格子。
    plantSeed:植物生成幾率,范圍:大於4的數,該值越小,生成的植物越多;該值越大,生成的植物越少。
    minerialSeed:礦物生成幾率,范圍:大於3的數,該值越小,生成的礦物越多;該值越大,生成的礦物越少。
    --]]
    gridCount = 50
    scaleX = 50
    scaleY = 50
    plantSeed = 20.0
    minerialSeed = 50.0
    
    -- 根據地圖大小申請圖像
    local w,h = (gridCount+1)*scaleX, (gridCount+1)*scaleY
    imgMap = image(w,h)
        
    -- 整個地圖使用的全局數據表
    mapTable = {}
    
    -- 設置物體名稱
    tree1,tree2,tree3 = "松樹", "楊樹", "小草"    
    mine1,mine2 = "鐵礦", "銅礦"
        
    -- 設置物體圖像
    imgTree1 = readImage("Planet Cute:Tree Short")
    imgTree2 = readImage("Planet Cute:Tree Tall")
    imgTree3 = readImage("Platformer Art:Grass")
    imgMine1 = readImage("Platformer Art:Mushroom")
    imgMine2 = readImage("Small World:Treasure")
    
    -- 存放物體: 名稱,圖像
    itemTable = {[tree1]=imgTree1,[tree2]=imgTree2,[tree3]=imgTree3,[mine1]=imgMine1,[mine2]=imgMine2}
       
    -- 3*3 
    mapTable = {{pos=vec2(1,1),plant=nil,mineral=mine1},{pos=vec2(1,2),plant=nil,mineral=nil},
                {pos=vec2(1,3),plant=tree3,mineral=nil},{pos=vec2(2,1),plant=tree1,mineral=nil},
                {pos=vec2(2,2),plant=tree2,mineral=mine2},{pos=vec2(2,3),plant=nil,mineral=nil},
                {pos=vec2(3,1),plant=nil,mineral=nil},{pos=vec2(3,2),plant=nil,mineral=mine2},
                {pos=vec2(3,3),plant=tree3,mineral=nil}}
    
end

-- 新建地圖數據表, 插入地圖上每個格子里的物體數據
function createMapTable()
    for i=1,gridCount,1 do
        for j=1,gridCount,1 do
            mapItem = {pos=vec2(i,j), plant=nil, mineral=nil}
            table.insert(mapTable, mapItem)
        end
    end
    updateMap()
end

-- 跟據地圖數據表, 刷新地圖
function updateMap()
    setContext(imgMap)   
    for i = 1,gridCount*gridCount,1 do
        local pos = mapTable[i].pos
        -- 繪制地面
        drawUnitGround(pos)
    end
    setContext()
end

-- 繪制單位格子地面
function drawUnitGround(position)
    local x,y = scaleX * position.x, scaleY * position.y
    pushMatrix()
    stroke(99, 94, 94, 255)
    -- 網格線寬度
    strokeWidth(1)
    -- 地面顏色
    fill(5,155,40,255)
    -- fill(5,155,240,255)
    rect(x,y,scaleX,scaleY)
    popMatrix()
end

-- 游戲主程序框架
function setup()
    displayMode(OVERLAY)

    initParams()
end

function draw()
    background(40, 40, 50)    
    
    -- 繪制地圖
    drawMap()
end

看看截圖:

只有地面的地圖原型

很好, 基本的格子地圖寫好了, 接着我們來解決在格子地圖上隨機插入樹木/礦物/建築等固定物體的功能.

插入物體

因為我們已經在設計數據表時就考慮到了要插入固定物體, 所以現在需要做的就是寫幾個相關的函數, 首先是兩個隨機選取物體名字的函數:

-- 隨機生成植物
function randomPlant()
    local seed = math.random(1.0, plantSeed)
    local result = nil
    
    if seed >= 1 and seed < 2 then result = tree1
    elseif seed >= 2 and seed < 3 then result = tree2
    elseif seed >= 3 and seed < 4 then result = tree3
    elseif seed >= 4 and seed <= plantSeed then result = nil end
    
    -- 返回隨機選取的物體名字
    return result
end

-- 隨機生成礦物
function randomMinerial()
    local seed = math.random(1.0, minerialSeed)
    local result = nil

    if seed >= 1 and seed < 2 then result = mine1
    elseif seed >= 2 and seed < 3 then result = mine2
    elseif seed >= 3 and seed <= minerialSeed then result = nil end
    
    -- 返回隨機選取的物體名字
    return result
end

然后增加兩個繪制函數, 來繪制出物體的圖像:

-- 繪制單位格子內的植物
function drawUnitTree(position,plant)
    local x,y = scaleX * position.x, scaleY * position.y
    pushMatrix()
    -- 繪制植物圖像
    sprite(itemTable[plant], x, y, scaleX*6/10,scaleY)
    
    --fill(100,100,200,255)
    --text(plant,x,y)
    popMatrix()
end

-- 繪制單位格子內的礦物
function drawUnitMineral(position,mineral)
    local x,y = scaleX * position.x, scaleY * position.y
    pushMatrix()
    -- 繪制礦物圖像
    sprite(itemTable[mineral], x+scaleX/2, y, scaleX/2, scaleX/2)

    --fill(100,100,200,255)
    --text(mineral,x+scaleX/2,y)
    popMatrix()
end

最后需要修改函數 createMapTable()updateMap(), 在其中增加對 plantmineral 的處理, 修改后的代碼如下:

-- 新建地圖數據表, 插入地圖上每個格子里的物體數據, 目前為 plant  和 mineral 為空
function createMapTable()
    --local mapTable = {}
    for i=1,gridCount,1 do
        for j=1,gridCount,1 do
            mapItem = {pos=vec2(i,j), plant=randomPlant(), mineral=randomMinerial()}
            --mapItem = {pos=vec2(i,j), plant=nil, mineral=nil}
            table.insert(mapTable, mapItem)
        end
    end
    updateMap()
end

-- 跟據地圖數據表, 刷新地圖
function updateMap()
    setContext(imgMap)   
    for i = 1,gridCount*gridCount,1 do
        local pos = mapTable[i].pos
        local plant = mapTable[i].plant
        local mineral = mapTable[i].mineral
        -- 繪制地面
        drawUnitGround(pos)
        -- 繪制植物和礦物
        if plant ~= nil then drawUnitTree(pos, plant) end
        if mineral ~= nil then drawUnitMineral(pos, mineral) end
    end
    setContext()
end

非常好, 第二個基本目標也完成了, 截個圖:

插入植物礦物的完整地圖

看看現在的截圖效果, 是不是感覺我們的原型正在一步步走向完善? 緊接着就要想辦法實現角色跟地圖上物體的交互了, 想做到這一點, 首先需要建立角色跟地圖在地圖數據表中的數據關聯.

建立角色跟地圖的關聯

現在地圖繪制好了, 角色也可以自由地在地圖上活動了, 不過這只是我們看到的表面現象, 實際在隱藏於屏幕后面的程序代碼中, 角色的位置跟地圖的坐標(方格)並沒有建立任何關聯.

例如, 角色在地圖上看到一棵樹, 他想要對這棵樹做一些動作(觀察/澆水/砍伐 等)進行交互, 如果角色選擇了砍伐樹, 那么最終樹被砍倒之后我們還需要更新地圖數據表, 把對應位置的樹的圖片更換成樹根, 而實現角色跟樹的交互, 就需要根據角色位置坐標跟樹的位置坐標進行判斷.

我們知道樹的位置坐標已經保存在地圖的數據表中了, 但是角色的坐標跟地圖的數據表還沒有任何關系, 因為角色經常移動, 所以我們可以寫一個函數, 根據角色的屏幕像素點坐標來計算所處的地圖方格坐標, 代碼如下:

-- 根據像素坐標值計算所處網格的 i,j 值
function where(x,y)
    local i = math.ceil((x+scaleX) / scaleX)
    local j = math.ceil((y+scaleY) / scaleY)
    return i,j
end

有了這個函數, 我們只要把角色當前位置的像素點坐標輸入, 就可以得到它所處網格的坐標, 這樣就把角色跟地圖從數據層面建立了關聯. 后續就可以方便地通過這個接口來處理他們之間的交互了.

為方便后續代碼維護, 我們要把上述代碼改寫為一個地圖生成類, 改寫后的完整代碼如下:

-- MapSample

Maps = class()

function Maps:init()
    --[[
    gridCount:網格數目,范圍:1~100,例如,設為3則生成3*3的地圖,設為100,則生成100*100的地圖。
    scaleX:單位網格大小比例,范圍:1~100,該值越小,則單位網格越小;該值越大,則單位網格越大。
    scaleY:同上,若與scaleX相同則單位網格是正方形格子。
    plantSeed:植物生成幾率,范圍:大於4的數,該值越小,生成的植物越多;該值越大,生成的植物越少。
    minerialSeed:礦物生成幾率,范圍:大於3的數,該值越小,生成的礦物越多;該值越大,生成的礦物越少。
    --]]
    self.gridCount = 50
    self.scaleX = 50
    self.scaleY = 50
    self.plantSeed = 20.0
    self.minerialSeed = 50.0
    
    -- 根據地圖大小申請圖像
    local w,h = (self.gridCount+1)*self.scaleX, (self.gridCount+1)*self.scaleY
    self.imgMap = image(w,h)
    
    -- 整個地圖使用的全局數據表
    self.mapTable = {}
        
    -- 設置物體名稱
    tree1,tree2,tree3 = "松樹", "楊樹", "小草"    
    mine1,mine2 = "鐵礦", "銅礦"
        
    -- 設置物體圖像
    imgTree1 = readImage("Planet Cute:Tree Short")
    imgTree2 = readImage("Planet Cute:Tree Tall")
    imgTree3 = readImage("Platformer Art:Grass")
    imgMine1 = readImage("Platformer Art:Mushroom")
    imgMine2 = readImage("Small World:Treasure")
    
    -- 存放物體: 名稱,圖像
    self.itemTable = {[tree1]=imgTree1,[tree2]=imgTree2,[tree3]=imgTree3,[mine1]=imgMine1,[mine2]=imgMine2}
       
    -- 尺寸為 3*3 的數據表示例
    self.mapTable = {{pos=vec2(1,1),plant=nil,mineral=mine1},{pos=vec2(1,2),plant=nil,mineral=nil},
                {pos=vec2(1,3),plant=tree3,mineral=nil},{pos=vec2(2,1),plant=tree1,mineral=nil},
                {pos=vec2(2,2),plant=tree2,mineral=mine2},{pos=vec2(2,3),plant=nil,mineral=nil},
                {pos=vec2(3,1),plant=nil,mineral=nil},{pos=vec2(3,2),plant=nil,mineral=mine2},
                {pos=vec2(3,3),plant=tree3,mineral=nil}}
    
    
    print("地圖初始化開始...")
    -- 根據初始參數值新建地圖
    self:createMapTable()
    print("OK, 地圖初始化完成! ")
end

-- 新建地圖數據表, 插入地圖上每個格子里的物體數據
function Maps:createMapTable()
    --local mapTable = {}
    for i=1,self.gridCount,1 do
        for j=1,self.gridCount,1 do
            self.mapItem = {pos=vec2(i,j), plant=self:randomPlant(), mineral=self:randomMinerial()}
            --self.mapItem = {pos=vec2(i,j), plant=nil, mineral=nil}
            table.insert(self.mapTable, self.mapItem)
        end
    end
    self:updateMap()
end

-- 根據地圖數據表, 刷新地圖
function Maps:updateMap()
    setContext(self.imgMap)   
    for i = 1,self.gridCount*self.gridCount,1 do
        local pos = self.mapTable[i].pos
        local plant = self.mapTable[i].plant
        local mineral = self.mapTable[i].mineral
        -- 繪制地面
        self:drawGround(pos)
        -- 繪制植物和礦物
        if plant ~= nil then self:drawTree(pos, plant) end
        if mineral ~= nil then self:drawMineral(pos, mineral) end
    end
    setContext()
end

function Maps:drawMap() 
    sprite(self.imgMap,-self.scaleX,-self.scaleY)
end

-- 根據像素坐標值計算所處網格的 i,j 值
function Maps.where(x,y)
    local i = math.ceil((x+self.scaleX) / self.scaleX)
    local j = math.ceil((y+self.scaleY) / self.scaleY)
    return i,j
end

-- 隨機生成植物
function Maps:randomPlant()
    local seed = math.random(1.0, self.plantSeed)
    local result = nil
    
    if seed >= 1 and seed < 2 then result = tree1
    elseif seed >= 2 and seed < 3 then result = tree2
    elseif seed >= 3 and seed < 4 then result = tree3
    elseif seed >= 4 and seed <= self.plantSeed then result = nil end
    
    return result
end

-- 隨機生成礦物
function Maps:randomMinerial()
    local seed = math.random(1.0, self.minerialSeed)
    local result = nil

    if seed >= 1 and seed < 2 then result = mine1
    elseif seed >= 2 and seed < 3 then result = mine2
    elseif seed >= 3 and seed <= self.minerialSeed then result = nil end
    
    return result
end

function Maps:getImg(name)
    return self.itemTable[name]
end

-- 重置  
function Maps:resetMapTable()
    self.mapTable = self:createMapTable()
end

-- 繪制單位格子地面
function Maps:drawGround(position)
    local x,y = self.scaleX * position.x, self.scaleY * position.y
    pushMatrix()
    stroke(99, 94, 94, 255)
    strokeWidth(1)
    fill(5,155,40,255)
    -- fill(5,155,240,255)
    rect(x,y,self.scaleX,self.scaleY)
    --sprite("Documents:3D-Wall",x,y,scaleX,scaleY)
    popMatrix()
end

-- 繪制單位格子內的植物
function Maps:drawTree(position,plant)
    local x,y = self.scaleX * position.x, self.scaleY * position.y
    pushMatrix()
    -- 繪制植物圖像
    sprite(self.itemTable[plant],x,y,self.scaleX*6/10,self.scaleY)
    
    --fill(100,100,200,255)
    --text(plant,x,y)
    popMatrix()
end

-- 繪制單位格子內的礦物
function Maps:drawMineral(position,mineral)
    local x,y = self.scaleX * position.x, self.scaleY * position.y
    pushMatrix()
    -- 繪制礦物圖像
    sprite(self.itemTable[mineral],x+self.scaleX/2,y,self.scaleX/2,self.scaleX/2)

    --fill(100,100,200,255)
    --text(mineral,x+self.scaleX/2,y)
    popMatrix()
end

-- 游戲主程序框架
function setup()
    displayMode(OVERLAY)

    myMap = Maps()
end

function draw()
    background(40, 40, 50)    
    
    -- 繪制地圖
    myMap:drawMap()
end

到目前為止, 我們在地圖生成原型章節的目標基本完成, 下一章我們會嘗試把 狀態 , 幀動畫地圖生成 這三個模塊整合起來, 一般來說事物發展到 的階段會由量變觸發質變, 我們這個程序也一樣, 會在這次整合之后, 從一個個零散簡陋的原型, 一躍而成一個還能看得過去的基本框架, 是不是很期待?

激動人心的新起點

事實上, 把角色屏幕位置跟地圖數據表建立關聯之后, 我們的角色就真正存在於這個游戲世界中了, 它可以自由地跟地圖上的每一個物體進行交互, 這意味着一個全新的激動人心的開始! 到現在為止, 我們游戲世界的基本框架已經搭建起來了, 我們可以在這個框架上試驗自己對於武俠冒險游戲的各種新想法.

所有章節鏈接

Github項目地址

Github項目地址, 源代碼放在 src/ 目錄下, 圖片素材放在 assets/ 目錄下, XCode項目文件放在 MyAdventureGame 目錄下, 整個項目文件結構如下:

Air:Write-A-Adventure-Game-From-Zero admin$ tree
.
├── MyAdventureGame
│   ├── Assets
│   │   ├── ...
│   ├── Libs 
│   │   ├── ...
│   ├── MyAdventureGame
│   │   ├──...
│   ├── MyAdventureGame.codea
│   │   ├──...
│   ├── MyAdventureGame.xcodeproj
│   │   ├──...
│   └── libversion
├── README.md
├── Vim 列編輯功能詳細講解.md
├── assets
│   ├── ...
│   └── runner.png
├── src
│   ├── c01.lua
│   ├── c02.lua
│   ├── c03.lua
│   ├── c04.lua
│   ├── c05.lua
│   ├── c06-01.lua
│   ├── c06-02.lua
│   ├── c06-03.lua
│   └── c06.lua
├── 從零開始寫一個武俠冒險游戲-0-開發框架Codea簡介.md
├── 從零開始寫一個武俠冒險游戲-1-狀態原型.md
├── 從零開始寫一個武俠冒險游戲-2-幀動畫.md
├── 從零開始寫一個武俠冒險游戲-3-地圖生成.md
├── 從零開始寫一個武俠冒險游戲-4-第一次整合.md
├── 從零開始寫一個武俠冒險游戲-5-使用協程.md
├── 從零開始寫一個武俠冒險游戲-6-用GPU提升性能(1).md
├── 從零開始寫一個武俠冒險游戲-6-用GPU提升性能(2).md
└── 從零開始寫一個武俠冒險游戲-6-用GPU提升性能(3).md

2 directories, 26 files
Air:Write-A-Adventure-Game-From-Zero admin$ 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM