PowerApps Game Tech No5
通信対戦を実装した。
— りなたむ || RYOTA NAKAMURA (@R_t_A_n_M) May 14, 2019
I built a game with a communication competition function using #PowerApps !!#PowerAppsGame#PowerPlatform#SQLServer#Azure#jpaug pic.twitter.com/QvUGlWGt30
通信対戦の仕組み
1.ゲームルームの作成及び参加
① 1P側がゲームルームを作成すると、Azure上のSQL Serverに生成したroom idと1Pの情報、ターン情報としてPendingとしてInsertする
② 2P側はターン情報がPendingとなっているroom id一覧を取得する
③ 選択したroom idのレコードに2Pの情報及びターン情報を1PTurnとしてUpdateする
2.ゲームの進行
① 1秒間隔で、参加中のroom idのレコードを参照する
② ターン情報が1PTurnとなっている場合にのみ攻撃ボタンが有効化される
③ 攻撃ボタンを押すと、1Pのステータスと2Pのステータス情報をもとに、ダメージ値を計算し、2PのHPを減算しUpdateする
④ ターン情報が2PTurnとなっている場合にのみ攻撃ボタンが有効化される
⑤ 攻撃ボタンを押すと、1Pのステータスと2Pのステータス情報をもとに、ダメージ値を計算し、1PのHPを減算しUpdateする
作り方
Azure SQL Server
SQL Serverの作成方法は割愛します。
作り方はこちらのようなサイトをご覧くださいませ
日商エレクトロニクスさんのブログ
テーブル構造
DDL文
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[PowerApps_NetBattle_Test_tbl](
[room_id] [char](50) NOT NULL,
[1PMAXHP] [int] NOT NULL,
[1PCurrentHP] [int] NOT NULL,
[1PPwr] [int] NOT NULL,
[1PDfs] [int] NOT NULL,
[1PSpd] [int] NOT NULL,
[1PLcy] [int] NOT NULL,
[1PMAXMP] [int] NOT NULL,
[1PCurrentMP] [int] NOT NULL,
[2PMAXHP] [int] NULL,
[2PCurrentHP] [int] NULL,
[2PPwr] [int] NULL,
[2PDfs] [int] NULL,
[2PSpd] [int] NULL,
[2PLcy] [int] NULL,
[2PMAXMP] [int] NULL,
[2PCurrentMP] [int] NULL,
[Turn] [char](10) NULL,
CONSTRAINT [PK_PowerApps_NetBattle_Test_tbl] PRIMARY KEY CLUSTERED
(
[room_id] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
PowerApps
0.App
アプリ起動時に実行される処理を定義します。
//global_valiable
Set(Dist_Point,20);
//1P_global_valiable
Set(Pre_1p_Max_HP,20);
Set(Pre_1p_Current_HP,Pre_1p_Max_HP);
Set(Pre_1p_Pwr,1);
Set(Pre_1p_Dfs,1);
Set(Pre_1p_Spd,1);
Set(Pre_1p_Lcy,1);
Set(Pre_1p_Max_MP,3);
Set(Pre_1p_Current_MP,3);
Set(Pre_1p_Dist_Point,Dist_Point);
Set(Pre_Room_ID,GUID());
//2P_global_valiable
Set(Pre_2p_Max_HP,Pre_1p_Max_HP);
Set(Pre_2p_Current_HP,Pre_2p_Max_HP);
Set(Pre_2p_Pwr,0);
Set(Pre_2p_Dfs,0);
Set(Pre_2p_Spd,0);
Set(Pre_2p_Lcy,0);
Set(Pre_2p_Max_MP,Pre_1p_Max_MP);
Set(Pre_2p_Current_MP,Pre_2p_Max_MP);
Set(Pre_2p_Dist_Point,Dist_Point);
1.SelectScreen
新しく部屋を作成するボタン
ボタンを押すと以下の処理が実行されるように宣言します。
Navigate(CreateRoom,ScreenTransition.Fade)
部屋に参加するボタン
ボタンを押すと以下の処理が実行されるように宣言します。
Navigate(JoinRoom,ScreenTransition.Fade)
2.CreateRoom
各パラメータを操作する部分はこちらの記事を参考に作成してください。
PowerAppsでバトルゲームを作ろう!(その1:パラメータ設定画面)
画面遷移ボタン
ボタンを押すと以下の処理が実行されるように宣言します。
Patch(
'[dbo].[PowerApps_NetBattle_Test_tbl]',
Defaults('[dbo].[PowerApps_NetBattle_Test_tbl]'),
{
room_id: Pre_Room_ID,
'1PMAXHP': Pre_1p_Max_HP,
'1PMAXMP': Pre_1p_Max_MP,
'1PCurrentHP': Pre_1p_Max_HP,
'1PCurrentMP': Pre_1p_Max_MP,
'1PPwr': Pre_1p_Pwr,
'1PDfs': Pre_1p_Dfs,
'1PSpd': Pre_1p_Spd,
'1PLcy': Pre_1p_Lcy,
Turn: "Pendding"
}
);
Set(
CurrentRoomID,
Pre_Room_ID
);
Navigate(
BattleView,
ScreenTransition.Fade
)
3.JoinRoom
参加する部屋の選択制御
部屋を選択するドロップダウンボックスはこのように宣言します。
Filter('[dbo].[PowerApps_NetBattle_Test_tbl]',Turn="Pendding").room_id
画面遷移ボタン
ボタンを押すと以下の処理が実行されるように宣言します。
Set(
CurrentRoomID,
Dropdown1.SelectedText.Value
);
Patch(
'[dbo].[PowerApps_NetBattle_Test_tbl]',
First(
Filter(
'[dbo].[PowerApps_NetBattle_Test_tbl]',
room_id = Dropdown1.SelectedText.Value
)
),
{
'2PMAXHP': Pre_2p_Max_HP,
'2PMAXMP': Pre_2p_Max_MP,
'2PCurrentHP': Pre_2p_Max_HP,
'2PCurrentMP': Pre_2p_Max_MP,
'2PPwr': Pre_2p_Pwr,
'2PDfs': Pre_2p_Dfs,
'2PSpd': Pre_2p_Spd,
'2PLcy': Pre_2p_Lcy,
Turn: "1PTurn"
}
);
Navigate(
BattleView,
ScreenTransition.Fade
)
4. BattleView
ステータス表示部
HP部分はこんな風になっています。
Concatenate(
"HP:",
Text(
First(
Filter(
'[dbo].[PowerApps_NetBattle_Test_tbl]',
room_id = CurrentRoomID
)
).'1PCurrentHP'
),
"/",
Text(
First(
Filter(
'[dbo].[PowerApps_NetBattle_Test_tbl]',
room_id = CurrentRoomID
)
).'1PMAXHP'
)
)
攻撃ボタン
攻撃ボタン(1PTurnと2PTurn)は、ボタン操作を制御するプロパティ(DisplayMode)と押したときに処理が実行されるプロパティ(OnSelect)を設定しています。
今回の処理は検証のために複雑なダメージ計算式などは除外、簡略化しています。
If(
First(
Filter(
'[dbo].[PowerApps_NetBattle_Test_tbl]',
room_id = CurrentRoomID
)
).Turn = "1PTurn",
DisplayMode.Edit,
Disabled
)
Set(
Pre_2p_Current_HP,
First(
Filter(
'[dbo].[PowerApps_NetBattle_Test_tbl]',
room_id = CurrentRoomID
)
).'2PCurrentHP' - 1
);
Patch(
'[dbo].[PowerApps_NetBattle_Test_tbl]',
First(
Filter(
'[dbo].[PowerApps_NetBattle_Test_tbl]',
room_id = CurrentRoomID
)
),
{
'2PCurrentHP': Pre_2p_Current_HP,
Turn: "2PTurn"
}
)
Set(
Pre_1p_Current_HP,
First(
Filter(
'[dbo].[PowerApps_NetBattle_Test_tbl]',
room_id = CurrentRoomID
)
).'1PCurrentHP' - 1
);
Patch(
'[dbo].[PowerApps_NetBattle_Test_tbl]',
First(
Filter(
'[dbo].[PowerApps_NetBattle_Test_tbl]',
room_id = CurrentRoomID
)
),
{
'1PCurrentHP': Pre_1p_Current_HP,
Turn: "1PTurn"
}
)
SQL Serverの情報取得タイマー
現在のレコードの内容を取得するタイマーです。
1秒毎に繰り返す設定をしています。
タイマーがスタートしたときに以下の処理が実行されるように宣言します。
Refresh('[dbo].[PowerApps_NetBattle_Test_tbl]')
データソースについて
今回異なるテナントでも通信対戦ができるようにとデータソースとしてSQL Serverを使用しました。一番格安なBasicプランでも、サクサク動作するようです。
使用者が増えたりした場合は順次Standardなどに切り替えればよろしいかと思います。
まとめ
やってる内容については実はあまり大したことをしていません。
仕組みも理解できれば案外単純なのか!とおもったり・・・。
実際はトランザクション処理などいろいろ大変だとは思いますが。使い方が個人利用レベルであればこの程度でも十分でしょう。
これをもとにバーコードバトラーを改造しようかなと考えています。