ゲームを題材に学ぶ 内部構造から理解するMySQL
第6回 DBサーバでゲームの処理を作るとどうなる?
本記事は,
本記事のテーマを,
DBサーバの負荷を軽減するためには
複数ユーザが同時にボスキャラと対戦するというゲームがあったそうですが,
そのため,
クライアントで処理を行うことには,
再現する仕様
複数のユーザが同時にボスキャラに対して攻撃を行います。ユーザアクションに対してダメージポイントはAPサーバ
リスト1 複数のユーザが同時にボスキャラに攻撃をする場合のテーブルを作成する
CREATE TABLE boss_status( id int AUTO_INCREMENT NOT NULL COMMENT 'ID' , boss_id int NOT NULL DEFAULT 0 COMMENT 'ボスキャラID' , hit_point int NOT NULL DEFAULT 0 COMMENT 'ヒットポイント' , status_id int NOT NULL DEFAULT 0 COMMENT 'ステータス(ビット)' -- …(中略)… , PRIMARY KEY CLUSTERED(id) ) COMMENT = 'ボスステータス';
状態異常時のステータスIDについては,
- 1b(1) → スタン
- 10b(2) → 麻痺
- 100b(4) → 毒
- 1000b(8) → 火傷
実装案:単純な更新処理
ボスキャラのステータスはビットで表現することで,
- 注1
- ビットOR演算とは,
2つの数値の同じ桁位置にあるビットのどちらか一方でも1の場合は1とし, それ以外の場合は0とした値になる演算処理のことです。
リスト2 ボスキャラのステータス変更時にビットOR演算を行うSQL
UPDATE boss_status SET hit_point = hit_point - CASE WHEN status_id & 2 = 2 THEN TRUNCATE(? * 1.2, 0) ELSE ? END , status_id = status_id | ? WHERE ID = ?;
特定の位置のビットが立っているかを確認するのは,
- 注2
- ビットAND演算とは,
2つの数値の同じ桁位置にあるビットの両方が1の場合は1とし, それ以外の場合は0とした値になる演算処理のことです。
実装案:ストアドファンクション,ストアドプロシージャ
ストアドファンクションとは,
実際のゲームのバトルはもっと複雑なのでしょうが,
リスト3 ストアドファンクションでの実装例
DELIMITER $$ CREATE FUNCTION get_hit_point( p_status_id int -- ボスの現在のステータスID , p_hit_point int -- ボスの現在のヒットポイント , p_given_status int -- 与えられたステータス , p_given_damage int -- 与えられたダメージ ) RETURNS int DETERMINISTIC BEGIN DECLARE v_Ratio float(3, 2); SET v_Ratio := 1; IF (p_status_id | p_given_status) & 2 = 2 THEN -- 2は麻痺 SET v_Ratio := 1.2; END IF; RETURN p_hit_point - TRUNCATE(p_given_damage * v_Ratio, 0); END; $$
ただし,
さらに,図1右の一連の処理をストアドプロシージャにしたものがリスト4で,
リスト4 ストアドファンクションを用い,
DELIMITER $$ CREATE PROCEDURE set_damage( p_id int -- ボスステータスID , p_given_status int -- 与えられたステータス , p_given_damage int -- 与えられたダメージ ) BEGIN UPDATE boss_status SET hit_point = get_hit_point( -- 作成したストアドファンクション status_id, hit_point -- boss_statusテーブルのカラム , p_given_status, p_given_damage -- パラメータ ) , status_id = status_id | p_given_status WHERE ID = p_id; -- ログなどの処理は省略 -- 現在の状態を返す SELECT * FROM boss_status WHERE ID = p_id; END; $$ DELIMITER ;
リスト5 APサーバがストアドプロシージャを呼び出すSQL文
CALL set_damage(?, ?, ?);
これで,
ストアドプロシージャは,
実際の処理を作ると,
ソースが分散する?
これらの実装案では,
というのも,
しかし,
「ソースが分散するから,
記事中で紹介した書籍
-
SQLの苦手を克服する本 データの操作がイメージできれば誰でもできる
本書は,SQLの文法は学んだもののSQLに苦手意識を持っているITエンジニアのための書籍です。 複雑なSQLを読める/書けるようになるには,データベースの表をカタマリで...