part1
・playerの攻撃を作成(当たり判定なし)までを解説
part2
・playerの常時ステータス表示windowとステータスバー(Hp,MP)までを解説
part3
・敵にHpとGoldを付与して、敵用のHpバーも作成するまでを解説
part4
・敵への攻撃判定までを実装するまでを解説
part5
・playerの攻撃の種類を増やして複数攻撃を実装までを解説
part6
・敵のAIや表示UIの調整から完成まで!!(最終回)
この講座を最後まで呼んでいただけると出来るゲームの内容(このゲームようなアクションRPGをつくります)
⇒ALL YOU NEED IS GOLD 0
今回でついに、このRPGMakerUniteでアクションRPGを作ろう!!シリーズは終了です!! ここまで読んで頂いた方がいたら本当にありがとうございます! 今回のこのpart6では敵のAIの作成。そして、後はメニュー画面を開いている時には攻撃出来ないとか、 今のところGOLDを手に入れても使い道がないので、そのGOLDでplayerを強化できるようにしていきます。 ここら辺に関してはuniteの側の操作であまりスクリプトはいじらないので、各自でuniteをいじって 好きなように、面白くゲームを作って行ってください!!
Uniteでは敵というか、イベントはUnite側で作成時に固定、ランダム、プレイヤーに近づく、遠のく、
決められたルートを移動と選べます。つまりこれらの挙動は元から用意されていることになりますね!
今回作成したいのは、普段はランダムに動いている敵が、playerを見つけたら(playerと敵の距離が一定以上近づいたら)
playerを追いかける、そしてplayerを見失ったら(playerとの距離が一定以上離れてら)ランダムな挙動を
示すAIにします。そして各敵の索敵能力(playerを見つけれる距離)は敵ごとに個別に指定出来るようにします。
必要な挙動はすべにuniteのスクリプトに用意されているはずですので、こちらで用意するのは
playerと敵の距離は計る関数、その距離によって挙動を変更する関数、敵それぞれに索敵する距離を設定する
関数を用意してあげれば出来そうですね!
それでは、その挙動はどのスクリプトで指定されているか探しましょう!unityの性質上EVENTの挙動を
支配するスクリプトは大体EVENTのobjectにアタッチしているはずなので、これを見ていると、
いかにもなスクリプトがありますね!
このMoveSetMovePointのスクリプトの中身をみてみると、このmoveKind変数でイベントの 挙動を指定出来るようです。現在はこのmoveKindはprivateになっているので、これを publicに変更して外部から参照出来るようにしましょう。
public int _moveKind; //ルート指定
これで_movekindが参照できるようになりました。それではこの_movekindを参照するための 仕組みを作成していきましょう!CharacterOnMap(ソースコード)の末尾に以下の二つの 関数を追加してください。
//独自に追加した関数------------------------------------------------------------
public GameObject GetEventGameObject() {
return this.gameObject;
}
public MoveSetMovePoint GetMoveSetMovePointScript() {
MoveSetMovePoint moveSetMovePointScript = GetEventGameObject().GetComponent<MoveSetMovePoint>();
return moveSetMovePointScript;
}
//-----------------------------------------------------------------/独自に追加した関数
この関数は何をしているかというと、CharacterOnMapについているので、今までの様に eventOnMaps[i].GetMoveSetMovePointScritpでEVENTのMoveSetMovePointをGetComponent して、movekindを簡単に参照できる様にしています。あとはplayerをEVENTが見つけているか のフラグもCharacterOnMapに追加してください。
//以下改変-----------------------------------------------------------------------------------------------------
public int my_EnemyHp;
public int my_EnemyGold;
//攻撃が死亡後も当たりGOLDを沢山取得できるのでflag管理追加
public bool death;
//enemyからplayerが見えているかのflag
public bool playerCanLook;
//----------------------------------------------------------------------------------------------------以下改変
これで下準備はできましたので、敵のAI用のスクリプトを作成していきましょう。
作成する場所はMy_EventManagerです。スクリプトは順次説明していきますが、
最後に分かりやすいように、長いですがMy_EventManager全文を載せます。
まずはplayerと敵との距離を測定する関数を追加しましょう。
//敵とplayerの距離を判定する関数
public bool CheckPlayerEnemyDistance(int i, float distance) {
Vector2 enemyPositon = eventOnMaps[i].GetCurrentPositionOnTile();
Vector2 playerPosition = MapManager.OperatingCharacter.GetCurrentPositionOnTile();
float enemyPlayerDistance = Vector2.Distance(enemyPositon, playerPosition);
if (enemyPlayerDistance < distance)
{
return true;
}
else
{
return false;
}
}
上記はplayerと敵との距離を測定して、引数distanceでしている距離より近いとtrue、遠いと falseを返します。つまり見えている時にtrue、見えてないときにfalseを返してくれる関数 ですね。次は上記での判定を利用して敵の挙動、つまりmoveKindを変更する関数を追加しましょう。
//距離によってenemyの行動を変更
public void EnemyAiController(int i, float CanLookDistance, float CannotLookDistance) {
if (CheckPlayerEnemyDistance(i, CanLookDistance))
{
moveSetMovePointScript = eventOnMaps[i].GetMoveSetMovePointScript();
moveSetMovePointScript._moveKind = 2;
eventOnMaps[i].playerCanLook = true;
}
else if (!CheckPlayerEnemyDistance(i, CannotLookDistance) && eventOnMaps[i].playerCanLook)
{
moveSetMovePointScript = eventOnMaps[i].GetMoveSetMovePointScript();
moveSetMovePointScript._moveKind = 1;
}
}
上記コードでは、先の関数CheckPlayerEnemyDistanceでtureつまり、playerが 見えている場合はmovekindに2を設定します。2がplayerを追跡です。そして playerを見つけているflagをtureにしています。falseの時はmovekindを1に しています。1でランダムです。後はそれぞれの敵の種類ごとにplayerを見つけれる 距離を指定する関数を作れは完成です。
// _moveKindは-1:固定、0:ルート指定、1:ランダム、2:追跡、3:プレイヤーから離れる
// -1からはどこに_moveKindを変えても動かない
public void CheckEnemyEvent() {
for (int i = 0; i < eventOnMaps.Count; i++)
{
//noteの内容で出来の処理の初期化を分岐
if (eventOnMaps[i].MapDataModelEvent.note == "gorotuki")
{
EnemyAiController(i, 2.0f, 3.0f);
}
else if (eventOnMaps[i].MapDataModelEvent.note == "touzokuM")
{
EnemyAiController(i, 4.0f, 5.0f);
}
}
}
上記コードは今までも使用してきた、EVENTのメモ欄によって、EnemyAiControllerの 引数を分けて、敵の索敵範囲を変更できるようにするスクリプトです。 これで敵のAI部分は完成になります。ここでMy_EventManagerの全文を長いですが載せておきます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//eventonmap
using RPGMaker.Codebase.Runtime.Map.Component.Character;
//mapeventexcutioncontroller
using RPGMaker.Codebase.Runtime.Map;
//datamanager
using RPGMaker.Codebase.Runtime.Common;
//RuntimeSaveDataModel
using RPGMaker.Codebase.CoreSystem.Knowledge.DataModel.Runtime;
//movesetmovepoint
using RPGMaker.Codebase.Runtime.Common.Component.Hud.Character;
public class My_EventManager : MonoBehaviour
{
private My_PlayerManagerScript my_PlayerManagerScript;
private GameObject player;
//playerCanvas
public Canvas my_PlayerCanvas;
private My_PlayerCanvasScript my_PlayerCanvasScript;
//enemyCanvas
public Canvas my_EnemyCanvas;
//eventonmap
private List<EventOnMap> eventOnMaps;
//playrのオブジェクトをstartで取得するとnullのエラーがでるのでこれを挟む
//またmap移動したときにplayerも破棄されるので再度initするためのフラグにしている
private bool first;
private MoveSetMovePoint moveSetMovePointScript;
// Start is called before the first frame update
void Start()
{
first = false;
my_PlayerManagerScript = this.gameObject.GetComponent<My_PlayerManagerScript>();
}
// Update is called once per frame
void Update()
{
EventManagerInitProcess();
if(player != null)
{
//playerがいるときに敵との距離判定する
CheckEnemyEvent();
if(CheckPlayerStatusChange())
{
SetPlayerStatus();
}
}
}
//startで呼ぶとうまくいかないのでこちらでEvent関連を初期化
public void EventManagerInitProcess() {
if (first == false)
{
if (Camera.main != null)
{
player = my_PlayerManagerScript.GetPlayerObject();
InitPlayeranvas(player);
EnemyMapEventInit();
first = true;
}
}
else if (first == true)
{
if (player == null)
{
Debug.Log("player lost");
//Mapが切り替わっているので初期化を呼ぶためフラグをfalse
first = false;
}
}
}
//playerのcanvasの初期化
public void InitPlayeranvas(GameObject player) {
Canvas my_PlayercanvasPrefab = Instantiate(my_PlayerCanvas);
my_PlayercanvasPrefab.transform.SetParent(player.transform, false);
my_PlayerCanvasScript = my_PlayercanvasPrefab.GetComponent<My_PlayerCanvasScript>();
//MaxHp,Hp設定
my_PlayerCanvasScript.playerMaxHp = my_PlayerManagerScript.GetPlayerMaxHp();
my_PlayerCanvasScript.playerHp = my_PlayerManagerScript.GetPlayerHp();
//Mp設定
my_PlayerCanvasScript.playerMaxMp = my_PlayerManagerScript.GetPlayerMaxMp();
my_PlayerCanvasScript.playerMp = my_PlayerManagerScript.GetPlayerMp();
//playerスライダーの初期化
my_PlayerCanvasScript.InitPlayerSlider();
Debug.Log("player init!");
}
//eventで作成している敵の初期化
public void EnemyMapEventInit() {
Debug.Log("my_controll start");
//全てのマップにあるイベントを取得
eventOnMaps = MapEventExecutionController.Instance.GetEvents();
Debug.Log("eventCount: " + eventOnMaps.Count);
for (int i = 0; i < eventOnMaps.Count; i++)
{
//noteの内容で出来の処理の初期化を分岐
if (eventOnMaps[i].MapDataModelEvent.note == "gorotuki")
{
EnemyEventInitProcess(i, 8, 15);
}
else if(eventOnMaps[i].MapDataModelEvent.note == "touzokuM")
{
EnemyEventInitProcess(i, 15, 30);
}
}
}
// EnemyMapEventInit()で仕様される共通の部分
public void EnemyEventInitProcess(int i, int hp, int enemyGold) {
//Hp設定
eventOnMaps[i].my_EnemyHp = hp;
//Gold設定
eventOnMaps[i].my_EnemyGold = enemyGold;
//死亡flagの設定
eventOnMaps[i].death = false;
//playerを見つけているかのflagの初期化
eventOnMaps[i].playerCanLook = false;
//self switchの取得 初期化もしている
var saveDataModel = DataManager.Self().GetRuntimeSaveDataModel();
var selfSwitchData = saveDataModel.selfSwitches.Find(selfSwitch => selfSwitch.id == eventOnMaps[i].MapDataModelEvent.eventId);
if (selfSwitchData == null)
{
selfSwitchData = new RuntimeSaveDataModel.SaveDataSelfSwitchesData();
selfSwitchData.id = eventOnMaps[i].MapDataModelEvent.eventId;
selfSwitchData.data = new List<bool>() { false, false, false, false };
saveDataModel.selfSwitches.Add(selfSwitchData);
}
//セルフスイッチAをtrueにしておく、Aがtureで敵が攻撃
selfSwitchData.data[0] = true;
selfSwitchData.data[1] = false;
selfSwitchData.data[2] = false;
//ターゲットのEVENTのgameObjectをイベントIDから取得
GameObject targetObject = MapEventExecutionController.Instance.GetEventMapGameObject(eventOnMaps[i].MapDataModelEvent.eventId);
//取得したゲームオブジェクトの子オブジェクトにプレハブのenemycanvasを入れる
Canvas enemyCanvasPrefub = Instantiate(my_EnemyCanvas);
//ここのfalseは後で変更必要?
enemyCanvasPrefub.transform.SetParent(targetObject.transform, false);
//enemyのHPの初期化 my_EnemyCanvasScriptはそれぞれ必要なのでその分用意
My_EnemyCanvasScript my_EnemyCanvasScript = enemyCanvasPrefub.GetComponent<My_EnemyCanvasScript>();
my_EnemyCanvasScript.enemyMaxHp = eventOnMaps[i].my_EnemyHp;
my_EnemyCanvasScript.enemyHp = my_EnemyCanvasScript.enemyMaxHp;
//sliderも初期化
my_EnemyCanvasScript.InitEnemyHpSlider();
}
// playerのステータスが変更になったタイミングを取得する関数
public bool CheckPlayerStatusChange() {
if (my_PlayerCanvasScript.playerMaxHp != my_PlayerManagerScript.GetPlayerMaxHp() || my_PlayerCanvasScript.playerHp != my_PlayerManagerScript.GetPlayerHp()
|| my_PlayerCanvasScript.playerMaxMp != my_PlayerManagerScript.GetPlayerMaxMp() || my_PlayerCanvasScript.playerMp != my_PlayerManagerScript.GetPlayerMp())
{
Debug.Log("change player status");
return true;
}
return false;
}
//playerのステータスを再設定する関数
public void SetPlayerStatus() {
//MaxHp,Hp設定
my_PlayerCanvasScript.playerMaxHp = my_PlayerManagerScript.GetPlayerMaxHp();
my_PlayerCanvasScript.playerHp = my_PlayerManagerScript.GetPlayerHp();
//Mp設定
my_PlayerCanvasScript.playerMaxMp = my_PlayerManagerScript.GetPlayerMaxMp();
my_PlayerCanvasScript.playerMp = my_PlayerManagerScript.GetPlayerMp();
//取得したステータスを反映
my_PlayerCanvasScript.setPlayerSlider();
}
//敵とplayerの距離を判定する関数
public bool CheckPlayerEnemyDistance(int i, float distance) {
Vector2 enemyPositon = eventOnMaps[i].GetCurrentPositionOnTile();
Vector2 playerPosition = MapManager.OperatingCharacter.GetCurrentPositionOnTile();
float enemyPlayerDistance = Vector2.Distance(enemyPositon, playerPosition);
if (enemyPlayerDistance < distance)
{
return true;
}
else
{
return false;
}
}
//距離によってenemyの行動を変更
public void EnemyAiController(int i, float CanLookDistance, float CannotLookDistance) {
if (CheckPlayerEnemyDistance(i, CanLookDistance))
{
moveSetMovePointScript = eventOnMaps[i].GetMoveSetMovePointScript();
moveSetMovePointScript._moveKind = 2;
eventOnMaps[i].playerCanLook = true;
}
else if (!CheckPlayerEnemyDistance(i, CannotLookDistance) && eventOnMaps[i].playerCanLook)
{
moveSetMovePointScript = eventOnMaps[i].GetMoveSetMovePointScript();
moveSetMovePointScript._moveKind = 1;
}
}
// _moveKindは-1:固定、0:ルート指定、1:ランダム、2:追跡、3:プレイヤーから離れる
// -1からはどこに_moveKindを変えても動かない
public void CheckEnemyEvent() {
for (int i = 0; i < eventOnMaps.Count; i++)
{
//noteの内容で出来の処理の初期化を分岐
if (eventOnMaps[i].MapDataModelEvent.note == "gorotuki")
{
EnemyAiController(i, 2.0f, 3.0f);
}
else if (eventOnMaps[i].MapDataModelEvent.note == "touzokuM")
{
EnemyAiController(i, 4.0f, 5.0f);
}
}
}
}
さて、これでゲームを起動してみてください!!playerが近づくとちゃんと敵が追跡してきますし、
接触による攻撃もしてきます。また離れるとランダムな挙動になり、また敵の種類ごとに
索敵範囲も個別に設定できることも分かります!
これで無事に敵AI完成です!
さて、現状では特にPRGでは必須の会話やお店が準備されていないので 分かりませんが、現状ですと以下の画像のように、いわゆるNPCとの会話中もplayerのステータスや 攻撃ボタンがでたままなので、例えば会話中にも攻撃できますし、会話中のステータスバーが 邪魔でNPCが見にくいなどの問題があるので解消していきます。
ゲーム中でメニューが開いているか等に関して色々管理しているソースがあります、
それがMenuManagerです、これは中をみればすぐに分かりますが、2つの便利そうな変数が
宣言されています。
IsMenuActiveとIsShopActive
上記の2つです。これらを利用してメニューが開いているときと、shopを開いている時に
player関連のUIの表示を切り替えればよさそうですね!これでできそうなものですが、
UIを非表示にしたいタイミングはもう一つあって会話中も非表示にしたいですよね!
これがかなり苦戦しました。頑張ってソースを探しましたが、会話中ですよって感じの
flagを見つけることが出来ませんでした。でもゲーム画面をよく見ると会話中になると
メニューを開くためのボタンが消えています。つまりソースでは何らかの方法でこのメニューボタン
の表示切り替えをしているはずです!!
そこで頑張って探しましたが、flagで簡単に管理されている感じでは無かったです。
このメニューボタンの管理はmenuBaseというソースコードにありました、そこでメニューを表示・
非表示を切り替えていたので、そこのタイミングに合わせて、こちらも新しいplayerUIを調整する
flagを立てて切り替えていく方法にしました。
ただ、この方法はあまり綺麗ではない感じの力業感があり、本当はもっと簡単に調整できるのかも
しれませんが、筆者はその方法を見つけることは出来ませんでした><。
それでは実装して行きましょう。まずはMy_SceneMapCanvasScriptに以下の関数を任意の場所に 追加してください。またその他の必要な変数も宣言してください。以下に変更後のMy_SceneMapCanvasScript 変更箇所までを記載します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
//inputhundler
using RPGMaker.Codebase.Runtime.Common;
//menumanager manebase
using RPGMaker.Codebase.Runtime.Map;
public class My_SceneMapCanvasScript : MonoBehaviour
{
public GameObject my_SceneMapManager;
private My_PlayerManagerScript my_PlayerManagerScript;
private My_EventManager my_EventManagerSript;
//display表示コントロール用
public GameObject buttonManagerObject;
public GameObject playerStatusManagerObject;
public TextMeshProUGUI playerHp;
public TextMeshProUGUI playerMp;
public TextMeshProUGUI playergold;
// Start is called before the first frame update
void Start()
{
my_PlayerManagerScript = my_SceneMapManager.GetComponent<My_PlayerManagerScript>();
my_EventManagerSript = my_SceneMapManager.GetComponent<My_EventManager>();
}
// Update is called once per frame
void Update()
{
DisplayPlayerStatus();
ControllDisplay();
}
public void DisplayPlayerStatus() {
playerHp.text = "HP: " + my_PlayerManagerScript.GetPlayerHp().ToString();
playerMp.text = "MP: " + my_PlayerManagerScript.GetPlayerMp().ToString();
playergold.text = "GOLD: " + my_PlayerManagerScript.GetPlayerGold().ToString();
}
//IsMenuActiveとIsShopActiveのみでは会話中のコントロールが出来ないので,Menubaseにmy_Canvas_showのflagを立てて対応
public void ControllDisplay() {
if(MenuManager.IsMenuActive || MenuManager.IsShopActive || !MenuManager.MenuBase.my_Canvas_show)
{
buttonManagerObject.SetActive(false);
playerStatusManagerObject.SetActive(false);
if(my_EventManagerSript.my_PlayercanvasPrefab != null)
{
my_EventManagerSript.my_PlayercanvasPrefab.enabled = false;
}
}
else
{
buttonManagerObject.SetActive(true);
playerStatusManagerObject.SetActive(true);
if (my_EventManagerSript.my_PlayercanvasPrefab != null)
{
my_EventManagerSript.my_PlayercanvasPrefab.enabled = true;
}
}
}
//以下省略・・・・・
}
まずはusingでMemuManager参照できるようにします。そしてdisplay表示コントロール用と コメントしているところにplayerの攻撃ボタンとplayerのHP等の表示部分をインスペクターから アタッチ出来るようにpublicで宣言しておきます。以下の画像の様にして緑線の部分を 宣言した部分にアタッチしてください。
そしてメインはControllDisplay()関数ですね。ここでIsMenuActiveとIsShopActiveを利用して
UIの調整をしています。そして
MenuManager.MenuBase.my_Canvas_show
上記のflagも併用しています。続いて上記のflagをmenuBaseに仕込んで行きましょう。
MeneBaseに仕込んだ前後を載せていきますので、参考にしながら皆さんのUniteのソース
コードのMenuBaseに変更を加えてください。
まずはメニューボタンと同じ様に挙動するように、my_Canvas_showというbool型の flagを設定しましょう。改変部にはコメントをつけています。
public MainMenu MainMenu;
public SkillMenu SkillMenu;
public SortMenu SortMenu;
public StatusMenu StatusMenu;
public bool MenuWillOpen = false;
//以下改変--------------------------------------------
public bool my_Canvas_show;
//-------------------------------------------------以下改変
//メニューが開けるかどうかの設定を行う
public void CanMenuOpen(bool canOpen) {
_canMenuOpen = canOpen;
_menuIcon.gameObject.SetActive(canOpen);
}
続いてこの変数を初期化しておきましょう。MenuBaseのstart関数の最後に以下を追加 してください。
protected void start()
{
//以上省略
//以下改変---------------------------
my_Canvas_show = true;
//------------------------------以下改変
}
つづいてLateUpdate関数に3つの変更を以下を参照しておこなってください。
public void LateUpdate() {
if (_canMenuOpen)
{
if (GameStateHandler.CurrentGameState() == GameStateHandler.GameState.MAP)
{
//マップ表示中
_menuIcon.gameObject.SetActive(true);
//以下改変---------------------------
my_Canvas_show = true;
//------------------------------以下改変
}
else if (GameStateHandler.CurrentGameState() == GameStateHandler.GameState.MENU && _mainObject.activeSelf)
{
//メインメニュー表示中
_menuIcon.gameObject.SetActive(true);
//以下改変---------------------------
my_Canvas_show = true;
//------------------------------以下改変
}
else
{
//その他のあらゆる状態
_menuIcon.gameObject.SetActive(false);
//以下改変---------------------------
my_Canvas_show = false;
//------------------------------以下改変
}
}
else
{
//メニュー自体が禁止の場合
_menuIcon.gameObject.SetActive(false);
}
if (_canMenuOpen)
{
if (InputHandler.OnDown(Common.Enum.HandleType.Back) || InputHandler.OnDown(Common.Enum.HandleType.RightClick))
{
BackMenu();
}
}
if (MenuManager.IsMenuActive)
{
if (InputHandler.OnDown(Common.Enum.HandleType.PageLeft))
{
ChangePage(false);
}
else if (InputHandler.OnDown(Common.Enum.HandleType.PageRight))
{
ChangePage(true);
}
}
}
最後にMemyBaseのMenuHidden関数を以下のように変更してください。
public void MenuHidden(bool iconDisplay) {
_menuIcon.SetActive(iconDisplay);
//以下改変------------------------------------------------
my_Canvas_show = iconDisplay;
//--------------------------------------------------以下改変
}
これでMeneBaseの変更は終了です。あとはmy_Player_CanvasPrefabの扱いの変更をすれば 完成です。My_EventManagerの冒頭で以下を宣言してください。
//生成後のplayerCanvas参照用
public Canvas my_PlayercanvasPrefab;
my_PlayercanvasPrefabはもともとInitPlayerCanvas関数でlocal変数と使用していたので、それを 変更します。
//playerのcanvasの初期化
public void InitPlayerCanvas(GameObject player) {
my_PlayercanvasPrefab = Instantiate(my_PlayerCanvas);
my_PlayercanvasPrefab.transform.SetParent(player.transform, false);
my_PlayerCanvasScript = my_PlayercanvasPrefab.GetComponent<My_PlayerCanvasScript>();
//MaxHp,Hp設定
my_PlayerCanvasScript.playerMaxHp = my_PlayerManagerScript.GetPlayerMaxHp();
my_PlayerCanvasScript.playerHp = my_PlayerManagerScript.GetPlayerHp();
//Mp設定
my_PlayerCanvasScript.playerMaxMp = my_PlayerManagerScript.GetPlayerMaxMp();
my_PlayerCanvasScript.playerMp = my_PlayerManagerScript.GetPlayerMp();
//playerスライダーの初期化
my_PlayerCanvasScript.InitPlayerSlider();
Debug.Log("player init!");
}
これで完成です。自分で会話イベントやshopイベントを作成して実行してみてください。
適切なタイミングでUI表示がOn Offされます。
これでUI表示に関しても完成です。
今までの部分でプログラミングしてソースを改変してって部分は終了です! 後は、今のままでは敵を倒してGOLDをゲットすることはできますが、そのGOLDの 使い道がアイテムを買うだけなので、主人公が強くなりません。 一般的には敵を倒した時に経験値を取得させることで成長させてもいいのですが、 このゲームの元のタイトルは「All You Need Id Gold」でした。GOLD取得のみで 主人公を強くしていく事を想定していました。Goldがあればアイテムは購入できる ので、アイテムで主人公を強くすればいいですね。Uniteのもとからあるアイテムで それが簡単に実現できます。以下の二つです。
上記を使用すればplayerのHpもMpも上昇させることができます。 これで解決ですが、後はもちろんゲームデザインの話ですが、今のところUniteでは個数制限したアイテムを 販売できないと思います。これをしないと、例えば最初の方の村で安く売っていたアイテムが無限に 買えると、レベルデザインに支障をきたしますよね。これは僕は以下の方法で解消しました。 この解消の仕方は、本当に人それぞれなので、本編終了後の後日譚的な感じで読んでください。 UniteのイベントとUnite側で設定できる関数を利用します。
やっていることは、プログラムと同じです。それが日本語になっているだけですね。 始まりの村の薬の数という変数を作成します。このUniteの変数は初期化をおそらくオープニングで しておくのがいいかと思います。なのでオープニングでこの変数に始まりの村で売りたい薬の数を設定して、 薬を買うたびに-1していって、この変数が0になったら販売終了にすればいいですね。 ここで示した例では、HP、Mp用の薬の管理を一つの変数で管理しているので、 これによって、ゲーム性が増しますかね?次の町までHP優先で行くのかMP優先か、はたまた バランス型か?みたいに。
先ほどは変数を利用しましたが、もちろん変数でもいいですが、Uniteにはスイッチもあります。 変数の参照の仕方はもう説明しましたが、スイッチも大体同じようなやり方で参照できます。 現状攻撃は2パターンですが、複数パターンにして特定のアイテムやイベントでスイッチを ONにして、このスイッチをスクリプトで監視しておけば、イベントやアイテムをきっかけに 新しい攻撃パターンを習得なんてこともできますよね!
さて、とっても長くなりましたが、このRPGMakerUniteでアクションRPGを作ろうもこれで完結です。
unite側のシステムを利用しているので、ここまでできれば後は攻撃を増やしたり、敵を増やしたり、
playerの攻撃方法を参考にして遠距離攻撃する敵など作れると思いますし、ストーリーや
町やmap、イベントもどんどん制作して立派なアクションRPGが作れると思います!
ここまで読んでいただき本当にありがとうございました!一人でもこの講座でゲームを
作る事やプログラミングに興味をもって頂けたら本望です!!
ありがとうございました!!!!!!!!!