iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 23
0
自我挑戰組

卡牌連線遊戲開發經驗分享系列 第 27

#26 遊戲實作(虛擬碼):新增玩家物件、開局處理

  • 分享至 

  • xImage
  •  

加入更多遊戲的實作,改用虛擬碼 (pseudo code) 撰寫,方便閱讀理解
系列文結束前應該會試著把遊戲內的機制實作出來,實作還是最重要的部份。

更新:

  1. 導入玩家物件、牌組、手牌、水晶、血量等遊戲場面資料
  2. 增加開局的處理:玩家開局抽牌、開局棄牌、配送幸運幣
  3. 調整遊戲狀態更新機制,全部由主控端決定,避免玩家發出不合法的指令

TODO:

  1. 實作生物牌、常用的觸發、生物效果
  2. 主控端檢查玩家行動是否合法?低優先度

主控端


開局設定場面狀態

翻面(i): return i==0 ? 1 : 0

玩家ID列表 = [p1uid, p2uid]
先手玩家序號 = 隨機整數(0,1) //決定先後手
先手玩家ID = 玩家ID列表[先手玩家序號]
後手玩家ID = 玩家ID列表[翻面(先手玩家序號 )]
玩家先後ID列表 = [先手玩家ID,後手玩家ID]
玩家行動序列 = 環形序列(玩家先後ID列表) //將列表轉換成環形序列
玩家物件列表 = [玩家(玩家先後ID列表[0]), 玩家(玩家先後ID列表[1])] //初始化玩家物件

玩家物件初始化

玩家.牌庫 = 牌組(API.取得牌組(deck_id))
玩家.職業 = 職業(API.取得牌組職業(deck_id))
玩家.血量 = 25
玩家.護甲 = 0
玩家.手牌 = 牌堆()
玩家.水晶 = 0
玩家.水晶槽容量 = 0
玩家.生物表 = 生物表()
玩家.祕密 = 祕密表() //聖騎士, 獵人, 法師 才有

//TODO 玩家view物件,跟玩家資料綁定

先手玩家的開局處理

三張牌 = 玩家物件列表[0].抽牌(3)
事件通知(0, 先手開場棄牌事件, 三張牌)
啟動事件迴圈(0, 先手選擇開場棄牌) //棄牌會放回牌庫重洗,雙方玩家完成棄牌後,正式開始回合

後手玩家的開局處理

四張牌 = 玩家物件列表[1].抽牌(4)
事件通知(1, 後手開場棄牌事件, 四張牌)
啟動事件迴圈(1, 後手選擇開場棄牌) //棄牌會放回牌庫重洗,雙方玩家完成棄牌後,正式開始回合
	事件通知(1, 配送幸運幣事件) //後手額外的效果

開始遊戲回合 (host)

回合序號 = 0
目前玩家序號 = 回合序號 % 2
等待玩家序號 = 翻面(回合序號)
目前玩家ID = 玩家行動序列.起始()
等待玩家ID = 玩家先後ID列表[翻面(回合序號 % 2)]

玩家回合(目前玩家序號):
	事件通知(目前玩家序號, 玩家行動事件)//啟動玩家的事件迴圈
	更新狀態_回合開始前(目前玩家序號)
	啟動事件迴圈(目前玩家序號, 玩家行動) //主控端接收玩家行動

	事件通知(等待玩家序號, 玩家等待事件) //啟動玩家的事件迴圈
	啟動事件迴圈(等待玩家序號, 玩家等待) //主控端不處理指令,保持佇列淨空

//接收到結束指令 or 強制結束時
目前玩家序號,等待玩家序號 = 等待玩家序號, 目前玩家序號
回合序號++

更新玩家狀態(回合開始前)

玩家 = 玩家物件列表[目前玩家序號]
if len(玩家.牌庫) > 0:
	一張牌 = 玩家.抽牌(1) //沒牌會創造疲勞卡,逐次上升
	事件通知(目前玩家序號, 抽牌事件, 一張牌)
else:
	玩家.疲勞值 = 1 if 玩家.疲勞值 is None else 玩家.疲勞值+1
	事件通知(目前玩家序號, 疲勞事件, 玩家.疲勞值)
	//狀態更新(目前玩家序號, "玩家血量", 玩家.HP - 玩家.疲勞值)

if 玩家.水晶槽容量 < 10:
	事件通知(目前玩家序號, 增加水晶槽容量, 1)
	玩家.水晶槽容量++
	//狀態更新(目前玩家序號, "水晶槽容量", 玩家.水晶槽容量)

//補滿水晶槽
狀態更新(目前玩家序號, "水晶", 玩家.水晶槽容量)
//遊戲內新增加的水晶會呈現比較亮的光澤,有空再實作

//手牌可使用
for 牌 in 玩家.手牌堆:
	if 牌.cost <= 玩家.水晶:
		牌.可使用 = true //同時 view 那邊會同步更新成綠框 react
	else:
		牌.可使用 = false

//生物可行動
for 生物 in 玩家.生物列表:
	生物.可攻擊 = true
	//TODO: 凍結狀態會阻止這個事件

//英雄能力可用
玩家.英雄能力.可使用 = true

更新玩家狀態(回合結束後)

//玩家回合結束事件的處理常式

玩家 = 玩家物件列表[目前玩家序號]
//關閉UI可用性
for 牌 in 玩家.手牌堆:
	牌.可使用 = false
for 生物 in 晚家.生物列表:
	生物.可攻擊 = false
玩家.英雄能力.可使用 = flase

更新玩家狀態(行動後)

//玩家操作事件後的處理常式
//TODO 

玩家端


玩家回合

回合開始的事件處理常式:
啟動玩家事件迴圈()
//直到玩家按下結束 or 超過90秒主控端強制結束

遊戲狀態的更新機制

事件通知(0, 先手開場棄牌事件, 三張牌)

有的事件會包含參數,代表會造成遊戲狀態的改變

  1. 主控端初始化,玩家初始化。H0, P0. host.init(), player.init()
    玩家無法得知牌庫內容,僅包含基本場面訊息

  2. 主控端開始遊戲後,產生第一個變化,告知行動玩家更新內容 H0>H1, host.publish(act_id, delta1,H1)
    等待玩家能看到牌背

  3. 玩家接收更新內容,回覆更新後的 hash值 作為驗證 P0>P1, player.ack(hash(P1))

  4. 玩家送出「行動指令」 player.commit(action_cmd)
    4-1. 主控端判定合法,廣播更新遊戲內容 H1>H2, host.publish(act_id, delta2,H2)
    4-2. 主控端判定不合法,回絕行動玩家的指令 host.reject(act_id, action_cmd)

簡短版:

  • 抽牌更新:更新、回應
  • 行動更新:行動、更新or回絕

上一篇
#25 工欲善其事,必先利其器 - 談遊戲開發環境&多人連線管理
下一篇
#27 研究筆記:BUFF系統
系列文
卡牌連線遊戲開發經驗分享30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言