【Unity・NodeJS】オンラインマルチプレイのシューティングゲームを一から作ってみる。【第1回】
どうも、こんにちは。
ただの大学生っていいます。
今回から、オンラインのマルチプレイが可能なシューティングゲームを、UnityとNodeJSを使ってゲームとサーバーを完全に1から作っていきたいと思います。
FPSのゲームなどはいつでも流行っていて自分で作ってみたと急に思ったので、作り始めたんですが、サーバーの開発などから行う記事などは見かけなかったので、自分で作っちゃおうとなりました。
基本的には分かりやすいように書いていくつもりですが、ある程度のUnityの知識がないと難しいので、Unityが初めてという方は、Unityの公式が出しているチュートリアルを使って一度ゲームを作ってみることをお勧めします。
Unity Learn : https://learn.unity.com/
また、Unityを使ったゲームの作り方の記事はいろいろあると思いますので、皆さん自分で作ってみることをお勧めします。
この記事は、最低限動くサーバーとゲームの開発を4回ぐらいかけて行うので、是非良ければ最後まで読んでみて下さい。
簡単な説明
ではまず初めに、どうやってマルチプレイを実現するかというのを簡単に説明したいと思います。
まず初めに、オンラインマルチプレイには様々な実現方法がありますが、今回は素直に「クライアントサーバーモデル」という、サーバーを作ってそこにプレイヤーがアクセスするという形にします。
ここでのサーバーというのは、プレイヤーの全部の位置情報を持っていて、それをプレイヤーに伝えるもので、クライアントというのはゲームを遊ぶ側です。
イメージ図:

ポイントとしては、サーバーは1つなのに対してクライアントはたくさんいることですね。
そして当たり前ですが、クライアントではゲームが遊べるように3Dの画面で敵を表示したり自分が動けるようにしないとだめですが、サーバー側は敵の位置情報などを伝えるだけでいいので、画面すらいらなかったりします。
ということで今回のこのマルチプレイの実現では、クライアント側ではゲームで遊べないとだめなので、Unityで3Dのゲームを開発し、そこで位置情報などをやりとりするサーバー側は、「NodeJS」というJavascriptで動くもので作成していきたいと思います。
そしてこれはまた次の記事でも詳しく書きますが、この画像内にある矢印はもちろん通信を表すのですが、この通信を実現するのには「websocket」というものを使って実現したいと思います。
また追々説明していきますので、とりあえず今回はこれぐらいの説明で終わっておきます。
クライアント側のゲームを簡単に作る
ではまず初めにUnityで3Dのプロジェクトを作りましょう。

Unityのバージョンとかは正直新しければなんでもいいと思います。(適当)

そして適当に床とプレイヤーになるカプセルを置いておきましょう。
今回はとりあえずプレイヤーを動かすところまでするので、次に今のカプセルに「Rigidbody」コンポーネントを取り付けましょう。

勝手にくるくる回られると困るので、「Freeze Rotation」を全部オンにしておきましょう。
とりあえずあとは操作をできるようにしたいので、「Player Controller」というスクリプトを作ってプレイヤーに取り付けましょう。
「Player Controller」のスクリプトに、プレイヤーを動かせるようなコードを書きましょう。
プレイヤーの動かし方には、transformを使った物や、Rigidbodyのaddforceやvelocityを変更する方法など様々ありますが、今回は素直にaddforceを使います。
正直これは私はどれでもいいじゃんと思っている派なので、皆さんも好きなやつとか自分が使ったことがあるやつで大丈夫です。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public Rigidbody rig;
private float PlayerMaxSpeed = 30f;
private float PlayerSpeed = 10f;
// Start is called before the first frame update
void Start()
{
rig = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
//移動
if (rig.velocity.magnitude < PlayerMaxSpeed)
{
Vector3 moveVector = new Vector3(0, 0, 0);
float tmpY = rig.velocity.y;
if (Input.GetKey(KeyCode.W))
{
moveVector = transform.forward * PlayerSpeed;
}
else if (Input.GetKey(KeyCode.S))
{
moveVector = transform.forward * -PlayerSpeed;
}
if (Input.GetKey(KeyCode.D))
{
moveVector = transform.right * PlayerSpeed;
}
else if (Input.GetKey(KeyCode.A))
{
moveVector = transform.right * -PlayerSpeed;
}
moveVector.y = tmpY;
rig.velocity = moveVector;
}
//ジャンプ
if (Input.GetKeyDown(KeyCode.Space))
{
var ray = new Ray(transform.position, Vector3.down);
var distance = 1.1f;
if (Physics.Raycast(ray, distance))
{
rig.AddForce(0, 300f, 0);
}
}
}
}
今回はこんな感じにしています。
とりあえず動けばいいやということでかなり雑になっているのは見て見ぬふりということで。

ということでプレイヤーの操作は完成しました。
あとは、一応FPSのゲームなので、カメラをプレイヤーにくっつけておきましょう。

「Create Empty」からPlayerというのを新しく作って、先ほど作ったものをBodyに変更しました。
「Rigidbody」と「Player Controller」は親のPlayerにくっつけて、最初に作ったものからは外しているので注意です。
また、視点移動のコードのために、カメラは頭にくっつけています。
あとはFPSなのでマウスで視点を変えれるコードを追加しておきます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public Rigidbody rig;
private float PlayerMaxSpeed = 30f;
private float PlayerSpeed = 10f;
public GameObject head;
// Start is called before the first frame update
void Start()
{
rig = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
float xRotation = Input.GetAxis("Mouse X");
float yRotation = -1 * Input.GetAxis("Mouse Y");
transform.Rotate(0, xRotation, 0);
head.transform.Rotate(yRotation, 0, 0);
//移動
if (rig.velocity.magnitude < PlayerMaxSpeed)
{
Vector3 moveVector = new Vector3(0, 0, 0);
float tmpY = rig.velocity.y;
if (Input.GetKey(KeyCode.W))
{
moveVector = transform.forward * PlayerSpeed;
}
else if (Input.GetKey(KeyCode.S))
{
moveVector = transform.forward * -PlayerSpeed;
}
if (Input.GetKey(KeyCode.D))
{
moveVector = transform.right * PlayerSpeed;
}
else if (Input.GetKey(KeyCode.A))
{
moveVector = transform.right * -PlayerSpeed;
}
moveVector.y = tmpY;
rig.velocity = moveVector;
}
//ジャンプ
if (Input.GetKeyDown(KeyCode.Space))
{
var ray = new Ray(transform.position, Vector3.down);
var distance = 1.1f;
if (Physics.Raycast(ray, distance))
{
rig.AddForce(0, 300f, 0);
}
}
}
}
左右の視点移動は、自分の体を移動させて実現し、上下の視点移動は頭を動かすことで実現しています。
これも自分自身がどの様にプレイヤーを動かしたいかで決めていただいて大丈夫です。

これでプレイヤーの準備はオッケーです。
サーバーにつなぐための準備をする
では今回の最後として、サーバーに繋ぐための準備をしておきます。
まずはゲームに「Create Empty」から「Network Manager」というものを作成しておきます。
そして、「NetworkManager」というスクリプトを作成して貼り付けておきましょう。

あとは、「Network Manager」からプレイヤーをスポーンできるようにスクリプトを作成しておきましょう。

「プレハブ化」というのでしょうか、とりあえず作成したプレイヤーを一度ファイルに動かして、ゲームからは消しましょう。
次に、NetworkManagerのコードを書き足して、ゲームが始まったらプレイヤーをスポーンさせるようにします。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NetworkManager : MonoBehaviour
{
public GameObject player;
// Start is called before the first frame update
void Start()
{
player = Instantiate(player);
}
// Update is called once per frame
void Update()
{
}
}
あとは、NetworkManagerにさっき作ったPlayerを入れて実行すれば、プレイヤーがゲームが始まると同時に出現するはずです。

第1回目はここまで
というわけでとりあえず今回はここまでにしましょう。
クライアント側のゲーム作成は、Unityで普通にゲームを作る感覚で進めていただいて大丈夫です。
次回からサーバー側を作成して、Unityのゲームと連動するようにしていきたいと思います。
次回はこちらから:
第2回目は作成中です。
もうしばらくお待ちください。
長くなってしまいましたが、
最後まで読んでいただきありがとうございました!
このブログでは、他にも初心者向けのプログラミングの記事やゲーム紹介の記事を書いていますので、良ければ是非読んでみて下さい!
おすすめ記事:

【わかりやすく解説】マインクラフトのJava版と統合版のマルチプレイの危険性について
どうも、こんにちは。ただの大学生です。 今回は、マインクラフトのマルチプレイの危険性についてお話していきたいと […]