PR

【DXライブラリ】敵を実装する

DXライブラリとC言語でゲーム制作。今回は敵の実装について。まず1体の敵を表示する機能を作ります。

敵の実装の土台

敵の実装だが基本プレイヤーの処理の流れと同じ。初期化や更新、描画、終了処理は今まで実装してきたのを敵用の変数に書き変えれば大半はOK。

プレイヤーと違う処理をする必要があるのはまずは移動。プレイヤーと違い自動で動くのでいろいろな処理を書くことになる。決まったところを往復する、ランダムに動く、静止しているがプレイヤーを検知すると追いかけてくるetc。

他には特定のタイミングでの敵の生成・消去処理など。特定の区間に入ったら出現させる(更新を始める)、HPが0になったら退場させるといったものが必要になるはず。あとRPGとかなら敵の思考ルーチンなども必要。

今回は叩き台としてマップ上に敵を1体出してテキトーに動くようにしてみる。プレイヤーとの当たり判定や出現・退場処理はまた今度。

サンプルコード

それでは実装。敵の処理を行う Enemy.cpp/h を新しく追加して以下のコードを貼る。

Enemy.h

#ifndef DEF_ENEMY_H

#define DEF_ENEMY_H

void Enemy_Initialize();
void Enemy_AngleUpdate();
void Enemy_MoveUpdate();
void Enemy_Animation();
void Enemy_Update();
void Enemy_Draw();
void Enemy_Finalize();

#endif

Enemy.cpp

#include "DxLib.h"
#include "Enemy.h"
#include "Map.h"
#include <math.h>
#include "Define.h"

//敵の構造体
typedef struct {
    int ModelHandle; //モデルハンドル
    VECTOR Position; //座標
    VECTOR TargetMoveDirection; //モデルが向くべき方向
    VECTOR MoveVec; //移動ベクトル
    float Angle; //モデルが向いている方向
    float JumpPower; //Y軸方向の速度
    int JumpStatus; //ジャンプ関連
    int Action; //現在の敵の行動
    int PrevAction; //1F前の敵の行動
    int AnimIndex; //任意のアニメーションの番号を取得するときに使う
    int AttachIndex; //アタッチするアニメーションの番号
    float TotalTime; //アニメーションの総再生時間
    float PlayTime; //アニメーションの再生時間

    int MoveTimer; //移動処理関連のタイマー
}enemy_t;

enemy_t enemy;

const static float ENEMY_MOVE_SPEED = 0.05f; //敵の移動速度
const static float ENEMY_ANGLE_SPEED = 0.2f; //敵の角度変化速度
const static float ENEMY_JUMP_POWER = 0.25f; //ジャンプ力
const static float ENEMY_GRAVITY = 0.01f; //敵の重力
const static float ENEMY_MAX_FALL_SPEED = -1.5f; //敵の落下速度の下限
const static float ENEMY_HIT_HEIGHT = 1.3f; //当たり判定用の高さ
const static int ENEMY_ANIMATION_NUM = 4; //敵のアニメーション総数

//敵の初期化
void Enemy_Initialize() {
    //座標
    enemy.Position = VGet(10.0f, 1.0f, 10.0f);

    //モデル読み込み
    enemy.ModelHandle = MV1LoadModel("dat/enemy/enemy00.mv1");

    //向く方向の初期化
    enemy.TargetMoveDirection = VGet(0.0f, 0.0f, 1.0f);

    //移動ベクトル初期化
    enemy.MoveVec = VGet(0.0f, 0.0f, 0.0f);

    //角度の初期化
    enemy.Angle = 0.0f;

    //ジャンプパワーの初期化
    enemy.JumpPower = 0.0f;

    enemy.JumpStatus = OBJ_NO_JUMP;

    //アクションの初期化
    enemy.Action = 0;
    enemy.PrevAction = 0;

    enemy.AttachIndex = 0;

    enemy.PlayTime = 0.0f;

    enemy.MoveTimer = 0;
}

//敵の向きを更新
void Enemy_AngleUpdate() {
    float TargetAngle; // 目標角度
    float SaAngle; // 目標角度と現在の角度との差

    // 目標の方向ベクトルから角度値を算出する
    TargetAngle = (float)atan2(enemy.TargetMoveDirection.x, enemy.TargetMoveDirection.z);

    // 目標の角度と現在の角度との差を割り出す
    {
        // 最初は単純に引き算
        SaAngle = TargetAngle - enemy.Angle;

        // ある方向からある方向の差が180度以上になることは無いので
        // 差の値が180度以上になっていたら修正する
        if (SaAngle < -DX_PI_F)
        {
            SaAngle += DX_TWO_PI_F;
        }
        else
            if (SaAngle > DX_PI_F)
            {
                SaAngle -= DX_TWO_PI_F;
            }
    }

    // 角度の差が0に近づける
    if (SaAngle > 0.0f)
    {
        // 差がプラスの場合は引く
        SaAngle -= ENEMY_ANGLE_SPEED;
        if (SaAngle < 0.0f)
        {
            SaAngle = 0.0f;
        }
    }
    else
    {
        // 差がマイナスの場合は足す
        SaAngle += ENEMY_ANGLE_SPEED;
        if (SaAngle > 0.0f)
        {
            SaAngle = 0.0f;
        }
    }

    // モデルの角度を更新
    enemy.Angle = TargetAngle - SaAngle;
    MV1SetRotationXYZ(enemy.ModelHandle, VGet(0.0f, enemy.Angle + DX_PI_F, 0.0f));
}

//敵の動きの更新
void Enemy_MoveUpdate() {
    enemy.MoveVec = VGet(0.0f, 0.0f, 0.0f);

    //一定時間X軸正方向に移動→X軸負の方向に移動を繰り返す
    if (enemy.MoveTimer < 180) {
        enemy.MoveVec = VGet(ENEMY_MOVE_SPEED, 0.0f, 0.0f);
    }
    else {
        enemy.MoveVec = VGet(-ENEMY_MOVE_SPEED, 0.0f, 0.0f);
    }

    enemy.MoveTimer++;
    if (enemy.MoveTimer > 360) enemy.MoveTimer = 0;

    //落下状態の計算
    if (enemy.JumpStatus == OBJ_FALL) {
        // Y軸方向の速度を重力分減算する
        enemy.JumpPower -= ENEMY_GRAVITY;

        // 落下速度が下限を超えていたら修正する
        if (enemy.JumpPower < ENEMY_MAX_FALL_SPEED) enemy.JumpPower = ENEMY_MAX_FALL_SPEED;

        // 移動ベクトルのY成分をY軸方向の速度にする
        enemy.MoveVec.y = enemy.JumpPower;
    }

    //マップとの当たり判定
    {
        VECTOR PolyPos1, PolyPos2;
        PolyPos1 = enemy.Position;
        PolyPos2 = VGet(PolyPos1.x, PolyPos1.y + 1.0f, PolyPos1.z);

        Map_CheckCollision(&enemy.Position, PolyPos1, PolyPos2, enemy.MoveVec, &enemy.JumpStatus, &enemy.JumpPower);
    }

    //敵の座標の更新
    MV1SetPosition(enemy.ModelHandle, enemy.Position);
}

//敵のアニメーション
void Enemy_Animation() {
    //プレイヤーのアニメーション処理を参考
}

//敵の更新
void Enemy_Update() {
    //敵の移動処理
    Enemy_MoveUpdate();

    //敵の方向を変える
    Enemy_AngleUpdate();

    //敵のアニメーション
    Enemy_Animation();
}

//敵の描画
void Enemy_Draw() {
    MV1DrawModel(enemy.ModelHandle);
}

//終了処理
void Enemy_Finalize() {
    MV1DeleteModel(enemy.ModelHandle);
}

基本は Player.cpp/h の使い回し。敵の構造体 enemy_t を作って座標や向きなどの情報を持たせ、移動や向き、アニメーションの更新を行う関数を用意する。

移動に関しては今回決まったところを往復するような感じにした。一定間隔で向きが変わるよう MoveTimer を用意して切り替えている。

これを土台にしていろいろな処理を追加していく。必要なのはプレイヤーなどの3Dモデルとの当たり判定処理や出現・消滅処理、行動ルーチンの設計などなど。これらについては後日記事に書いていく予定。

コメント

タイトルとURLをコピーしました