【unity1week】「ふえる」に参加しました!
はじめに
2020年8月10日〜16日に unity1week が開催されました。 お題は「ふえる」。投稿数は過去最高で500を超えました! 参加された皆さん、お疲れ様でした!!!
自分が作ったものはコチラになります!
また、リポジトリも公開しているので、アドバイスなどありましたらよろしくお願いします!!!
今回のunity1weekではどのように作っていたかを振り返りたいと思います
目標
公開時間(16日20時)に間に合わせる
前回(「つながる」)よりも良い評価をいただく
これだけです!
制作過程
当初の予定では
- 1日目 企画決め
- 2~5日目 ゲームフローの作成
- 6日目 演出・音など
- 7日目 予備日(公開日)
のように進めるつもりでしたが、だいぶ狂ってしまいました(自ら狂わせたんですけどね...)
実際の制作過程です
1日目 企画決め
簡単なルール / 操作でテンポがいい(リトライしやすい)ゲームにしようと思いましたが、全然思いつきませんでした
そこで!
【Unity道場 5月 〜ゲームジャムで役立つ超速ゲーム企画〜】を参考に
と決めました
2日目 ゲームフローの作成①
物理挙動あるあるですね
エッジコライダーとポリゴンコライダーを間違えてました...
あぁ〜コレコレ...! #unity1week pic.twitter.com/3m1AzTHRdH
— キタ (@atasfrznnks) 2020年8月11日
3日目 ゲームフローの作成②
「ふやす」ができました
今回のコア部分! 個人的に気に入っています!
スコアっぽいものが付きました #unity1week pic.twitter.com/apSWUPKol7
— キタ (@atasfrznnks) 2020年8月12日
4日目 ゲームフローの作成③
ゲームっぽくなりました
が、つまらない... どうすれば面白くできるのか...
とりあえずのゲームフローができました〜 #unity1week pic.twitter.com/zetdwTeJYK
— キタ (@atasfrznnks) 2020年8月13日
5日目 オンライン対戦の追加①
ということで(?)、思いつきでオンライン対戦を組み込むことに!
マッチング周りについてはほぼ過去のコードを再利用しています(以前に作っててよかった〜)
無事マッチングできたので勝ち #unity1week pic.twitter.com/PqdXCeQX9B
— キタ (@atasfrznnks) 2020年8月14日
6日目 オンライン対戦の追加②
ターン切り替えのタイミングが悪い結果...
これは仕様です 両者敗北にはならないのでセーフ
2人とも勝てるケースがある幸せな『対戦』ゲームになりました #unity1week pic.twitter.com/CuFE5WiJ58
— キタ (@atasfrznnks) 2020年8月15日
7日目 ラストスパート
余裕がなくて進捗ツイートできてなかったのですが、20時には間に合いました!
この日は「見た目」や「演出」を主にやってました
\(^o^)/オワタ #unity1week pic.twitter.com/DK0jRLMMsa
— キタ (@atasfrznnks) 2020年8月16日
10日目 後日談
これで両者勝者にならなくなりました
が!たまに判定中で止まってしまうことがあり...(コライダーのめり込みが原因?)
コライダーのめり込みが発生しにくいような工夫はしたのですが、解決できませんでした...
オンライン対戦時に静止判定のフェーズを追加しました〜(少しは遊びやすくなったハズ...) #unity1week pic.twitter.com/IOsNA1fNzK
— キタ (@atasfrznnks) 2020年8月19日
結果
116名の方から評価を、25名の方からコメントをいただきました。(9月3日22時)
遊んでくださった方、実況配信してくださった方、それにコメントしてくださった方
ありがとうございました!!!
さて、気になる評価の結果は...
楽しさ
- 3.767
- 自分でも楽しめるようなゲームにできなかったので、次回は頑張ろうと思います(次回っていつの話になるんだろう?)
絵作り
- 3.905
- ふやした時のエフェクトとかでしょうか?
- 絵作りムズカシイ...
-
- 3.733
- DOVA-SYNDROME様、効果音ラボ様ありがとうございました!
操作性
- 4.147 🎉第9位🎉
- コメント等をみると操作感も含まれているのかな、と思いました
雰囲気
- 3.81
- 雰囲気もムズカシイ...
斬新さ
- 3.44
- 個人的には皆無だと思っていたので、もっと低いと思っていました
さいごに
目標は達成できましたし、モチベもかなり上がりました、ありがとうございました
次回は「絵作り」「雰囲気」で良い評価をいただけるように頑張りたいと思います
オンライン対戦作るならレート機能作るべきだった
【Unity】ObjectPoolをちょっとだけラクに実装する
はじめに
Unityではオブジェクトの生成・破棄は負荷が大きいため、大量に呼び出すとフレームレートの低下(最悪の場合は処理落ち)してしまいます。
そこでオブジェクトプールというオブジェクトの生成に関するパターンがあります。これはオブジェクトの数を制限し、再利用したい時に利用するパターンです。
少し制限がありますが、その分ちょっとラク?に実装する方法を紹介します。
実装する
ObjectPoolクラス
抽象クラスとして扱います。
using System.Collections.Generic; using UnityEngine; /// <summary> /// ObjectPool /// </summary> /// <typeparam name="T">Component限定</typeparam> public abstract class ObjectPool<T> : MonoBehaviour where T : Component { /// <summary> /// Poolしたいオブジェクト /// </summary> [SerializeField] private T poolObject; /// <summary> /// PoolのためのList /// </summary> private List<T> _poolList; /// <summary> /// Poolするオブジェクトの親オブジェクト /// </summary> [SerializeField] private Transform poolParent; /// <summary> /// 初期化時にPoolする数 /// </summary> [SerializeField] private int initializePoolCount; protected virtual void Awake() { _poolList = new List<T>(); for (int i = 0; i < initializePoolCount; i++) { var pool = CreatePool(Vector3.zero, Quaternion.identity); pool.gameObject.SetActive(false); } } /// <summary> /// Poolを新しく生成 /// </summary> /// <param name="createPosition"></param> /// <param name="createRotation"></param> /// <returns></returns> private T CreatePool(Vector3 createPosition, Quaternion createRotation) { var newPoolObject = Instantiate(poolObject, createPosition, createRotation); newPoolObject.transform.parent = poolParent; _poolList.Add(newPoolObject); return newPoolObject; } /// <summary> /// Poolしたオブジェクトから再利用 or 新しく生成 /// </summary> /// <param name="setPosition"></param> /// <param name="setRotation"></param> /// <returns>Poolしたオブジェクト</returns> protected T ActivatePool(Vector3 setPosition, Quaternion setRotation) { foreach (var pool in _poolList) { if (pool == null) { continue; } if (pool.gameObject.activeSelf) { continue; } pool.transform.position = setPosition; pool.transform.rotation = setRotation; pool.gameObject.SetActive(true); return pool; } return CreatePool(setPosition, setRotation); } /// <summary> /// Poolした全オブジェクトの破棄 /// </summary> protected void ClearAllPool() { foreach (var pool in _poolList) { Destroy(pool.gameObject); } _poolList.Clear(); } }
ObjectPoolを扱うクラス
Playerなど、Poolするオブジェクトを生成するclassです。
ObjectPool<PoolしたいオブジェクトのComponent>
を継承する必要があります。
Poolする対象をComponentに限定しているため、GameObject型
などは扱えません。
今回はTestBulletをPoolする対象とします。
using UnityEngine; public class TestPlayer : ObjectPool<TestBullet> { private Transform _generateTransform; protected override void Awake() { // Awakeで何か宣言したい場合↓を呼ぶ必要アリ // base.Awake(); } private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { // Poolしたオブジェクトの利用 var pos = _generateTransform.position; var rot = _generateTransform.rotation; ActivatePool(pos, rot); } } }
Poolする対象
利用時の初めに必要な処理がある場合はOnEnable()
を呼びます。
また、破棄したいタイミングではDestroy()
の代わりにSetActive(false)
を呼びます。
using UnityEngine; [RequireComponent(typeof(Rigidbody))] public class TestBullet : MonoBehaviour { private Rigidbody _rigidbody; private void Awake() { _rigidbody = GetComponent<Rigidbody>(); } private void OnEnable() { _rigidbody.velocity = transform.forward; } private void OnTriggerEnter(Collider other) { gameObject.SetActive(false); } }
さいごに
説明不足な部分が多々あると思いますが、ご容赦ください。
また、間違いやアドバイス等ありましたらドシドシお願いします!!!
UniRxにあるObjectPoolの方が便利です(早く覚えないと...!)
【Unity】PlayMaker完全に理解する (1)
(注意)タイトルと本編は一切関係ありません
今回の目標
「PlayMakerを用いて以下の処理を行う」
using UnityEngine; public class PlayerMovement : MonoBehaviour { private void Update() { if (Input.GetKey(KeyCode.W)) { transform.position += transform.forward; } else if (Input.GetKey(KeyCode.S)) { transform.position -= transform.forward; } } }
目標のプログラムの分解
目標のプログラムを分解すると以下のようになります
using UnityEngine; public class PlayerMovement : MonoBehaviour { private Vector3 currentPosition; private Vector3 moveDirection; private bool isW; private bool isS; private void Update() { isW = Input.GetKey(KeyCode.W); isS = Input.GetKey(KeyCode.S); if (isW) { currentPosition = transform.position; // get position moveDirection = transform.forward; // set move direction currentPosition += moveDirection; // set next position transform.position = currentPosition; // update position } else if (isS) { currentPosition = transform.position; // get position moveDirection = transform.forward; // set move direction currentPosition -= moveDirection; // set next position transform.position = currentPosition; // update position } } }
PlayMakerで実装する
(1) Playerオブジェクト
に PlayerMovement
というFSMを追加👇
(2) 分解したプログラムを参考に変数を追加👇
(3) Input
MoveForward
MoveBack
の3つの状態を作成👇
(4) InputW
InputS
ToInput
というイベントを追加👇
(5) 状態Input に InputW
InputS
の遷移を追加し、4つのアクションを設定👇
Get Key
Keyを押している間 true、その結果をStore Resultに格納
Bool Changed
Bool Variable の値が変化した時、Change Event の状態に遷移
(6) 状態MoveForward に ToInput
の遷移を追加し、2つのアクションを設定👇
(7) ゲームシーンを再生し、Wキーを押すと状態が遷移するのを確認👇
(8) 標準では transform.forward
transform.up
transform.right
が無いっぽい?ので
Assets/PlayMaker/Actions/Transform に GetDirectionVector.cs
を追加👇
// 参考 https://hutonggames.com/playmakerforum/index.php?topic=41.0 using UnityEngine; namespace HutongGames.PlayMaker { [ActionCategory(ActionCategory.Transform)] [Tooltip("Get the Direction (Forward, Up, Right) Vector of the Transform")] public class GetDirectionVector : FsmStateAction { public enum Direction { Forward, Up, Right } public Direction direction; [RequiredField] public FsmOwnerDefault gameObject; [UIHint(UIHint.Variable)] public FsmVector3 vector; [UIHint(UIHint.Variable)] public FsmFloat x; [UIHint(UIHint.Variable)] public FsmFloat y; [UIHint(UIHint.Variable)] public FsmFloat z; public bool everyFrame; public override void Reset() { gameObject = null; vector = null; x = null; y = null; z = null; direction = Direction.Forward; everyFrame = false; } public override void OnEnter() { DoGetForward(); if (!everyFrame) Finish(); } public override void OnUpdate() { DoGetForward(); } private void DoGetForward() { var go = Fsm.GetOwnerDefaultTarget(gameObject); if (go == null) return; var directionVector = Vector3.zero; switch (direction) { case Direction.Forward: directionVector = go.transform.forward; break; case Direction.Up: directionVector = go.transform.up; break; case Direction.Right: directionVector = go.transform.right; break; } if (directionVector == Vector3.zero) return; vector.Value = directionVector; x.Value = directionVector.x; y.Value = directionVector.y; z.Value = directionVector.z; } } }
(9) 状態MoveForward に4つのアクションを設定👇
Get Position
GameObject の Space 座標を Vector に格納
Get Direction Vector
GameObject の Direction(Right、Up、Forward)をVectorに格納
Vector3 Add
Vector Variable に Add Vector を加算
Set Position
GameObject の Space 座標に Vector に代入
Wキーを押している間は前方に移動します(やった!)
(10) 状態MoveBack にも状態MoveForward と同様に設定👇
Vector3 Subtract
Vector Variable から Subtract Vector を減算
(閉じているアクションは状態MoveForwardと同じです)
これにて完成!!!
もう少し簡単に実装する
Get Key
に拘らなければもう少し簡単に実装することができます
(1) 状態Input のアクションを修正👇
Get Key Down
Key を押した時、Send Event の状態に遷移
(2) 状態MoveForward のアクションを修正👇
Get Key Up
Key を離した時、SendEventの状態に遷移
(3) 状態MoveBack も同様に修正👇
この修正により、bool型の変数 isW
isS
が不要になります
完全に理解するまでにはまだまだ時間がかかりそうですが、引き続きガンバリマス!!!
【Unity】PlayMaker完全に理解する (0)
(注意)タイトルと本編は一切関係ありません
PlayMakerのみでゲームを作れるようになることを目標にガンバリマス。
そもそもPlayMakerって何?
Unityのビジュアルスクリプティングのアセットです。
直感的なビジュアルエディタ
デバッグが強力
多くのアドオンが利用可能
などが強みです。
使用ソフトのバージョン
本ブログでの開発環境は以下の通りになります。
macOS Mojave 10.14.5
Unity 2019.1.0f2
PlayMaker 1.9.0.p16
導入方法
1 AssetStoreからPlayMakerをインポート
2 以下の手順を踏んでPlayMakerをインストール
3 メニュー欄から PlayMaker > PlayMaker Editorを選択
下図のようなPlayMakerタブが出てきます。
以上で導入は終了です。
次回👇
オススメ資料
ユニティ・テクノロジーズ・ジャパン合同会社様が作成した資料👇
www.slideshare.net
STYLY様が作成した資料👇
【unity1week】「つながる」に参加しました!
はじめに
2019年3月11日〜17日に開催されたunity1weekに参加しました。
今回のお題は「つながる」ということで、ゲーム内容は「つなげる」でしたが、オンラインで「つながる」なので、問題ないはずです。。。
以下制作したゲームです。(2人用)
制作過程
- 月 企画が定まらない
- 火 なんとなく作り始める、プレイヤーの操作とか
- 水 ゲーム開始の演出、ゲーム性がなさすぎて絶望する
- 木 UI、ゲームオーバー追加、絶望しつつも制作を続ける
- 金 少しでもゲーム性を見出すためにギミック追加
- 土 ギミック追加してもゲーム性を見出せなかったので協力プレイにすることに、Photon導入
- 日 音・エフェクトの追加
といった感じでなんとか間に合いました!
制作過程の反省
良かった点
- 汎用性のあるシステムを作っていた
- Photonを使用するのは3回目であったため、戸惑う部分が少なかった
ダメだった点
- 企画を定めずに作り始めたこと(唯一かつ最大のダメポイント)
- デバッグプレイが不十分であった
ゲーム内容の反省
良かった点
- 協力プレイのため、複数人で楽しめる
- ゲームのテンポがいいのでリトライしやすい
ダメだった点
- 球の位置の同期でカクつく
- コンボ数の同期が不十分
- 球から真下に出ている線がわかりにくい
- etc...(言い出したらキリがない)
さいごに
評価してくれた41名の皆様、ありがとうございました。
次回は今回以上に良い評価を得られるようにガンバリマス!!!
【Unity】Stateパターンっぽい何かで状態遷移の実装
はじめに
AをしたらBに、BをしたらCに、CをしたらAに...
このようなケースをどのように処理すればいいかの一例として紹介します(既出の場合はすみません)
今回は以下のような3点を移動する処理を作成したいと思います
interfaceの作成
以下のようなインターフェースを作成します
public interface IState { void Initialize(); // 各状態での変数の初期化 void Execution(); // 各状態の実行 }
各状態の作成
原点に移動するコードです
using UnityEngine; public class ToOrigin : MonoBehaviour, IState { private Vector3 originPos; public void Initialize() { originPos = Vector3.zero; } public void Execution() { // 移動完了後、次のstateへ if (transform.position == originPos) { NextState(); } transform.position = Vector3.MoveTowards(transform.position, originPos, Time.deltaTime); } public void NextState() { StateManager.NextState(); } }
残りの2つの状態は上記のコードと酷似しているため省略します
これらのコードは動かすcubeにアタッチします
状態を管理するマネージャーの作成
using System; using System.Linq; using UnityEngine; // 実行したいstateの数だけ用意 public enum State { origin, right, up, } public class StateManager : MonoBehaviour { private IState[] stateList; // 全stateを格納する配列 private static int stateCount; // stateの数 private static State currentState; // 実行するstate private void Start() { stateCount = Enum.GetValues(typeof(State)).Length; stateList = new IState[stateCount]; SetStateList(); // 全stateの初期化 stateList.ToList().ForEach(state => state.Initialize()); currentState = State.origin; } // 全stateの取得 private void SetStateList() { stateList[(int)State.origin] = FindObjectOfType<ToOrigin>(); stateList[(int)State.right] = FindObjectOfType<ToRight>(); stateList[(int)State.up] = FindObjectOfType<ToUp>(); } private void Update() { stateList[(int)currentState].Execution(); } // stateの遷移 public static void NextState() { currentState = (int)currentState == stateCount - 1 ? 0 : currentState + 1; } }
説明不足な部分が多々あると思いますが、ご容赦ください
また、何かアドバイス等ありましたらドシドシお願いします!!!