2015年の「ゴマちゃんフロンティア」まとめ

というわけで、2015年も後わずかとなってまいりました。
そんな年の瀬、今年の「ゴマちゃんフロンティア」についてまとめてみようと思います!

歴史としては開設が2014年2月なので、1年と10ヶ月ですね。
更新ペースは平均して1週間に1回くらいです。
元々趣味で始めただけでしたが、少しずつPVが上がってきており、新ブログ移行前には1日30~50となりました。
(新ブログへの移行に若干失敗気味なので今は微妙ですが・・・)
閲覧者の皆様、本当にありがとうございます!

ブログ的な変化とまとめ

最も大きい変化は「ブログサービス移行」でしょう。
9月まではFC2ブログサービスを使用していましたが、10月からはレンタルVPS+WordPressの独自運用に切り替えました。
ブログ名も2代目ということで、「ゴマちゃんフロンティアV2」に改名しました。

WordPressのテンプレートに合わせ、サイトの雰囲気も今ブームなフラットデザインに近くなりました。
何よりレスポンシブデザインなので、スマフォからでも問題なく見れるようになったのは大きいです。
その他プラグイン等もあり、運用していくのが快適になっています。

実はまだ旧ブログのインデックスがGoogle上に残っていたりします。
完全な移行にはまだ時間が掛かりそうです。

ゲーム的な変化とまとめ

春あたりから開発方針をいろいろ変えたのが一番大きいです。
プレイヤーキャラを絞ったり、スクリプト構成を変えたり、カメラアングルを固定にしたり・・・などなど。
細かく修正したり追加している要素もあります。

メインキャラ3匹も度々モデル修正を行っているため、結構変わっていたりします。
古いマリンパとシルリスの画像があったので、現在のモデルを見比べてみました!

shot2ss20150326192942424

shot2ss20151231204746805

20150512_shilriss

shot2ss20151231204849845

全体的に靴が付いた分、頭身が上がったような感じです。
最も変わったのは上のマリンパでしょうか。
逆にモグフィーは変わらなすぎるためか、画像が残っていませんでしたorz

キャラクター性能もころころ変わっており、マリンパは近距離、モグフィーは空中、シルリスは遠距離に強みを持たせていこうと思います。
RPGのように3人で得意不得意を分担するようなイメージです。
性能バランスを取るのは後回しで完全を優先させます。

逆にそれ以外ではあまり変化がなかったりします。
変わりすぎても困る上、今から大きな変化を加えるのもアレなので、当然と言えば当然ではあります。
「変える」より「作る」ことを重視したいですorz

来年の抱負

何はなくとも「ゲームにする」ことですね。
ゲーム性やボリュームを度外視してでも1つの作品として遊べるようにしたいです。
後々からでも修正・追加ができるよう作っている(つもり)なので尚更です。

また、ブログ的にはUnity一辺ではなく、別カテゴリの記事にも手を付けようかと考えていたりします。
具体的にはサーバ構築やLinux、PHPあたりのお話です。
既に「行ってきました」シリーズなど趣旨がずれている記事もあるので、いっそ開き直って他も充実させていきたいです!
もちろんメインは「Unityを使ったゲーム開発日記」のままで。

まとめ

そんなわけで、2015年のゴマちゃんフロンティアをまとめてみました。
見返すとざっくり且つほとんどまとめになっていませんが気にしないようにします!

これが2015年最後の記事となりそうです。
皆さん良いお年を!

【開発メモ】アシストキャラクターの実装 その2

というわけで、今回は前に実装したアシストキャラクターの続きになります!
前回の記事はこちら

【開発メモ】アシストキャラクターの実装 その1

前回でアシストキャラクターへの乗り降りまで実装したので、今回は実際にアシストキャラクターを操作する部分を実装してみようと思います!
基本操作はプレイヤーに合わせ、移動・攻撃・ジャンプなどの基本アクションを作っていきます。
処理を流用できる部分も多いため、実装だけならあまり苦戦はしなかったです。

移動系

基本的な移動はプレイヤーと同じく、CharacterMotorとPlatformInputControllerで行います。
今はアザラシをベースに作っているため、移動速度は少し遅めにしてみます。

また、アシストキャラには回避や二段ジャンプなどの特殊な移動は実装しない予定です。
そういう意味ではプレイヤーキャラに比べて単純かもしれません。
その分各キャラに特殊な能力を付けたいと思いますが、それはまた追々ということで!

攻撃系

攻撃判定の生成ロジックはプレイヤーと一緒です。
参考になりそうな過去記事はこちら

【再編集】Unity開発メモまとめ 「攻撃処理系」

【開発メモ】攻撃判定制御のリファクタリング

同じこと書いてもしょうがないので、アザラシに持たせる攻撃方法について考えます。
これは前々から決めていて、強力な遠距離攻撃を持たせる予定です。
通常攻撃は高威力の弾を発射し、特殊攻撃には敵を追尾するレーザーを放ちます。

shot2ss20151226120459482

使わなくなったパーティクルで良いものがあったため移植し、通常攻撃としました。
口から出すには弾が大きい気がしますが、あまり気にしないことにします!
特殊攻撃のレーザーはロジックが難しそうなので後回しにします。

問題点

今回までの実装で抱えている問題点を挙げてみます。

乗り降りの際の円形エフェクトの表示

円形エフェクトはEffekseerを使って表示していますが、エフェクト毎に個別でインスタンス化されているわけではなく、Effekseerオブジェクトから全体をコントロールしている(?)ようです。
それ故、「特定のエフェクトだけを有効・無効にする」ということが困難です。

EffekseerのUnityマニュアルを見てみると、コンポーネントの他にスクリプトからでも再生ができるようです。
これを使えば個別でエフェクトをコントロールできそう。
使う機会は多いと思われるため、EffectUtilityというユーティリティクラスを作成し、その中でハンドルを生成する関数を実装します。

using UnityEngine;
using System.Collections;

public static class EffectUtility {
    public static EffekseerHandle createEffekseerHandle(string fileName, Transform transform) {
        EffekseerHandle handle = EffekseerSystem.PlayEffect(fileName, transform.position);
        handle.SetRotation(transform.rotation);
        handle.SetScale(transform.localScale);

        return handle;
    }
}

このEffekseerHandle型にはStop()という関数が含まれており、これを実行することで止めることができます。
もっと効率の良い方法がありそうですが、とりあえずこれで解決!

AnimatorのAnyStateからの割り込み

ある意味最大の問題点です。
アシストキャラに乗っている場合、プレイヤーキャラは待機orダメージモーションのみで他のモーションは入りません。
が、AnyStateから設定されているダメージや回避などは割り込んで再生されてしまいます。

Controllerを動的に切り替えることができれば行けそうですが、Animatorを動的に変えること自体がかなり怖いです。
遷移しないようにスクリプトから無理矢理制御できなくもなさそうです。
とてもスマートとは言えませんが・・・。

CharacterControllerの当たり判定

アザラシは横長な動物なわけですが、CharacterControllerのカプセルコライダーは横向きにすることができません。
仕方なくHeightを小さくし、Radiusで調整するようにしましたが・・・

shot2ss20151226120714778

見ての通り胴体前後の判定がスッカスカです。
NPCの敵が相手なので致命的ではありませんが、これが対戦ゲームとかだったらクレーム殺到なレベルですね。
CharacterController自体の判定は移動制御のみに使い、別でダメージ判定用のコライダーを付けれれば何とかなりそうです。

まとめ

今まで「まとめ」が全然まとめじゃなかったので、今度から箇条書きでやったこと/分かったことを書いていくようにします!

・アシストキャラクターの基本コントロールはプレイヤーと同じ
・アザラシは遠距離型キャラクターにする
・Effekseerはスクリプトから生成することでhandleから個別に停止できる
・いろいろと問題あり

細かい部分に難がありますが、見てくれだけは一応完成。
上手くゲームシステムやステージ構成に組み込んで行きたいです。

今日のイラスト

またまたシルリスちゃんです。
輪郭はパスツールで、影はペンタブ+エアブラシで塗ってみました!
マウスで適当に影を付けるよりは味が出ている・・・気がする。

shilriss5

身延町西嶋イルミネーションを見に行ってきました!

というわけで、新ブログでの「行ってきました」シリーズ第2弾です。
今回は身延町西嶋のイルミネーションに行ってきました!
イルミネーション自体は前々から催されていますが、近年はメディアに取り上げられることも多くなり、知名度が上がってきている感じです。

CIMG1341_R

周辺に駐車場はいくつかありますが、自分は中富和紙の里に停めました。
5時前に着きましたが、既に20台ほど停まっていました。
駐車場や入場料は掛からない上、水団や柚子汁が無料で提供されています。

イルミネーションの通りまで徒歩で10分ほどです。
距離はさておき寒いので、防寒対策はしっかりしておいた方が良いと思われます。

以下、ひたすらイルミネーションの写真です!

CIMG1342_R

CIMG1345_R

CIMG1349_R

CIMG1355_R

CIMG1360_R

CIMG1366_R

静止画で見ると普通に感じますが、実際は流れるように点滅しているので綺麗です。
2015年は1月3日までやっているそうなので、足を運べる方は行ってみると良いと思います!

【開発メモ】アシストキャラクターの実装 その1

というわけで、前回の投稿から時間が空いてしまいましたが、今回のお題はこれです!

shot2ss20151215210405763

新要素として「アシストキャラクター」を実装してみようと思います!
プレイヤーキャラとは別にステージ上にいる仲間キャラで、乗ることで操作することができるというものです。

元々はプレイヤーキャラとしてアザラシやカンガルーも入れようとしたのですが、開発中に厳しいと判断し3キャラに絞った経緯があります。
そのキャラを何とかゲームに組み込めないかと思い、「ステージ上で一時的に操作できるキャラ」という結論に至りました。

今回はプレイヤーキャラがアシストキャラに乗り降りするシステムを作ってみます。
焦っても雑になってしまうので、まったりやっていきます!

下準備

当たり前ですが、アシストキャラクターのモデルを作る必要があります。
ということでさくっと作ります!

shot2ss20151215210217326

作成したアザラシ(のつもり)です。
カンガルーとどちらにしようか迷いましたが、アザラシの方が乗るのに違和感なさそうという点と、単純に自分が大好きな動物なのでチョイスしました!
見直すとあまりアザラシっぽくないですが、そのうち修正します!
今回は乗り降りだけなので、モーションも作っていません。

乗る場合の処理

乗っていないアシストキャラには円形のエフェクトを出し、その範囲内でボタンを押すことでプレイヤーキャラが飛び乗るようにします。
アシストキャラの位置はプレイヤーの操作以外で動かすことはないと思われます。

円形の判定はアシストキャラクター本体に子オブジェクトとして作ります。
エフェクトはEffekseerのサンプルを参考にを作成・使用しています。
専用のスクリプト「RideArea」を作成します。

using UnityEngine;
using System.Collections;

public class RideArea : MonoBehaviour {

    private AssistCharacter assistCharacter;

    public void OnTriggerStay(Collider c) {
        if (TagUtility.getParentTagName(c.gameObject) == "Player") {
            if (Input.GetButtonDown("Action")) {
                Player player = c.GetComponent<Player>();
                assistCharacter.setPlayer(player);
                player.onRideOnAssistCharacter(assistCharacter);
            }
        }
    }
}

判定内でボタンが入力された場合、Playerクラスの「onRideOnAssisstCharacter」を実行します。
プレイヤーキャラをアシストキャラの上に飛び乗るように移動させ、transform.parentにアシストキャラを設定して子オブジェクトにします。
また、各種コントロール系を無効化します。

public void onRideOnAssistCharacter(AssistCharacter assistCharacter) {
    // コントロール系の無効化
    charControl.enabled = false;
    charMotor.enabled = false;
    platForm.enabled = false;

    // プレイヤーのtransform修正
    transform.parent = assistCharacter.transform;
    transform.rotation = assistCharacter.transform.rotation;

    // ジャンプ時のモーションパス定義
    var pathList = new List<Vector3>();
    pathList.Add(transform.position);
    pathList.Add((transform.position + assistCharacter.ridePoint.transform.position) / 2 + new Vector3(0f, 10f, 0f));
    pathList.Add(assistCharacter.ridePoint.transform.position);

    iTween.Stop(gameObject);
    iTween.MoveTo(gameObject, iTween.Hash(
        "path", pathList.ToArray(),
        "time", 1f,
        "delay", 0.01f,
        "easetype", "easeOutSine",
        "oncomplete", "onPlayerRideOn",
        "oncompletetarget", assistCharacter.gameObject
    ));
}

ジャンプ時の移動処理はiTweenで行います。
アシストキャラクターの子オブジェクトとして「ridePoint」という空オブジェクトを用意しておきます。
iTween.MoveTo()の移動先は”position”で指定しますが、今回は”path”を使ってみました!
Vector3型のListを作り、「プレイヤーキャラ→ジャンプの頂点→ridePoint」となるように挿入してします。
あとは”easetype”を”easeOutSine”にすればジャンプが放物線っぽい挙動になります。

iTween.Stop()とdelayを入れている理由は以下の記事を参照して頂ければと思います。

【Unity】iTweenの停止と再実行について

最後はアシストキャラ本体に付与するスクリプトを作成します。
以下はAssistCharacterクラスですが、実際にはそれを継承したSealクラスを付けています。
現状ではスーパークラスの関数を呼んでいるだけなのでほとんど意味はありませんが、後々増やすことも考え、専用に作っておきます。

Start()やUpdate()は特に面白いことをしていないので、プレイヤーが乗った際の関数のみ記載します。

public void onPlayerRideOn() {
    isRiding = true;

    charControl.enabled = true;
    charMotor.enabled = true;
    platForm.enabled = true;
}

関数はiTweenの実行後に呼ばれるよう、oncompleteで指定します。
isRidingフラグをONにし、各種コントロールを有効化します。

プレイヤーの参照としてPlayerクラスも渡して設定しておきます。
どのプレイヤーが乗っているか把握したり、何かと処理で使う機会が多いためです。

降りる場合の処理

乗っている最中にボタンを押すことで飛び降ります。
飛び降りる方向はアシストキャラの後ろ側です。
後ろが崖の状態で飛び降りるとそのまま落下しそうですが、現状ではどうしようもない部分なので気にせずいきます!

AssistCharacterクラスのUpdate()内で「ボタン押下かつ地面にいる状態」を判定し、降りる際の処理を記述した関数を呼んでいきます。
降りた後はアシストキャラを操作しなくなるので、コントロール系を無効化します。

public void onPlayerRideOff() {
    isRiding = false;

    charControl.enabled = false;
    charMotor.enabled = false;
    platForm.enabled = false;

    player.onRideOffAssistCharacter(this);
}

アシストキャラの処理後にPlayerクラスの「OnRideOffAssistCharacter」を実行します。
iTweenで飛び降りる処理と、コントロールを有効化する処理を書きます。

public void onRideOffAssistCharacter(AssistCharacter assistCharacter) {
    // コントロール系の有効化
    charControl.enabled = true;
    charMotor.enabled = true;
    platForm.enabled = true;

    // ジャンプ時のモーションパス定義
    Vector3 assistCharBack = assistCharacter.transform.position - (assistCharacter.transform.forward * 10f);
    var pathList = new List<Vector3>();
    pathList.Add(assistCharacter.ridePoint.transform.position);
    pathList.Add((transform.position + assistCharBack) / 2 + new Vector3(0f, 10f, 0f));
    pathList.Add(assistCharBack);

    transform.parent = null;
    iTween.Stop(gameObject);
    iTween.MoveTo(gameObject, iTween.Hash(
        "path", pathList.ToArray(),
        "time", 1f,
        "delay", 0.01f,
        "easetype", "easeOutSine"
    ));
}

「キャラクターの後ろ側に移動する」という点でちょっと詰まりました。
transform.positionを元にtransform.forward + Vector3() で上手くずらすような感じになります。
transofrm.forwardをそのまま参照すると原点近くまで吹っ飛んでしまうので気をつけます。

iTweenは乗る際と同じく”path”で指定します。
今度は「ridePoint→ジャンプの頂点→アシストキャラの背後」となるように設定してあります。

20151215

実際に乗り降りするとこんな挙動になります!
概ね自分の想定していた挙動になってくれてよかったです。

まとめ

そんなわけで、アシストキャラクターと乗り降りの部分を作ってみました!
次はアシストキャラクターを操作する部分を実装していく予定です。
難しそうなので遅れるかもしれませんが、まったりやります!

【ステージメモ】ステージギミックの作成

というわけで、本日はステージ上のギミックに関するお話です!
ステージの地形や敵配置も重要ですが、ステージギミックを合わせてこそアクションゲームというものです。

あまりにも鬱陶しいギミックを仕掛けても面倒な上、そのような代物を作る知識もありません。
ということで、オーソドックスなものから実装してみました!

ダメージトラップ

触れるとダメージを受けるものです。
オーソドックスなトゲや、高温地帯のマグマなど、いろいろ考えられそうです。

中でも代表的なのは「うに」です!

shot2ss20151202223931357

何故「うに」かと言えば、海が(一応)テーマのゲームだからです。
それ以上でも、それ以下でもありません。
純粋に痛そうでもあり、球状なので全方位をカバーできます。

ちなみにモデルはBlenderのDuplication機能を使って作成しました。
球状のオブジェクトの頂点にコーン状のオブジェクトを複製しています。

処理的には接触時にPlayerクラスのDamage()を呼び出すだけです。
が、谷底など落ちたら復帰できない場所に配置してあるものは、接触したら手前に戻すようにしたいですね。
なので、空の子オブジェクトとして「returnPoint」を設定し、接触時にプレイヤーの位置をそこに戻すようにします。

using UnityEngine;
using System.Collections;

public class Trap : MonoBehaviour {

    public float damage;

    private Transform returnPoint;

    void Start () {
        returnPoint = transform.FindChild("returnPoint");
    }

    private void OnTriggerEnter(Collider c) {
        if (c.gameObject.tag == "Player") {
            c.GetComponent<Player>().damage(damage);
            if (returnPoint) {
                StartCoroutine("playerReturn", c.gameObject);
            }
        }
    }

    private IEnumerator playerReturn(GameObject player) {
        yield return new WaitForSeconds(1f);
        player.transform.position = returnPoint.position;
    }
}

いちいちGetComponent()しているのが微妙なところです。
SendMessage()でも良いのですが、後々引数が増えそうなので直接呼んでいます。

returnPoint がないトラップはワープを行わないようにします。
if文で適当に制御してあげれば良いと思われます。

ジャンプエリア

該当の判定内に入っている状態でジャンプすると、大ジャンプが行えるものです。
ステージに高低差を付けたり、純粋なアクション操作を要求するギミックとして使います。

shot2ss20151202224351021

分かりやすいようにエフェクトを付けてみました。
Effekseerの描画方法をリングにし、頂点数を4にすることで正方形が作れます。
その正方形に合うようにBoxColliderを調整します。

using UnityEngine;
using System.Collections;

public class JumpArea : MonoBehaviour {

    public float height;
    private Player player;
    
    protected void OnTriggerEnter(Collider c){
        if(c.gameObject.tag == "Player"){
            player = c.GetComponent<Player>();
            player.getCharacterMotor().jumping.baseHeight = height;
        }
    }
    
    protected void OnTriggerExit(Collider c){
        if(c.gameObject.tag == "Player"){
            player.getCharacterMotor().jumping.baseHeight = 5f;
        }
    }
}

やっていることはすごく単純で、Enter時にCharacterMotorのジャンプ関係の値を上げるだけ。
Exitした時にデフォルト値に戻さないとずっと維持されてしまうので気を付けます。

Effekseerの良い点として、Unity側での拡大・縮小に合わせてエフェクトが拡縮するところが挙げられます。
様々な大きさのジャンプエリアを作る場合でも対応できます。

リフト

一定間隔で上下左右に動くリフトです。
リフトをコントロールするためのクラスを作成し、リフト用オブジェクトに付けます。

using UnityEngine;
using System.Collections;

public class Lift : MonoBehaviour {
    
    public float speed, repeat_dist, delay;
    public int moveDirection;

    private float radian;
    private Vector3 defaultPosition;
    private bool isMoveing;
    
    void Start () {
        defaultPosition = transform.position;
        radian = 0;

        if (delay > 0) {
            StartCoroutine("startDelay", delay);
        } else {
            isMoveing = true;
        }
    }
    
    void Update () {
        if(isMoveing){
            switch (moveDirection) {
                case 0:
                    transform.position = defaultPosition + new Vector3(Mathf.Sin(radian) * repeat_dist, 0, 0);
                    break;
                case 1:
                    transform.position = defaultPosition + new Vector3(0, Mathf.Sin(radian) * repeat_dist, 0);
                    break;
                case 2:
                    transform.position = defaultPosition + new Vector3(0, 0, Mathf.Sin(radian) * repeat_dist);
                    break;
            }
            radian += speed * Time.deltaTime;
        }
    }
    
    IEnumerator startDelay(float time){
        yield return new WaitForSeconds(time);
        isMoveing = true;
    }
}

配置位置からの反復距離をrepeat_distとして設定します。
また、XYZのどの軸に対して移動するかを選択できるようにします。
直打ちするのもアレなので、インスペクターからプルダウンで選択できるよう、エディタ拡張用のクラスを作成します。

 
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(Lift))]
public class LiftEditor : Editor {

    private static readonly string[] Directions = {"X", "Y", "Z"};

    public override void OnInspectorGUI(){
        var lift = target as Lift;

        lift.speed = EditorGUILayout.FloatField
                ("Speed", lift.speed);
        lift.repeat_dist = EditorGUILayout.FloatField
                ("repeat_dist", lift.repeat_dist);
        lift.delay = EditorGUILayout.FloatField
                ("delay", lift.delay);
        lift.moveDirection = EditorGUILayout.Popup
                ("MoveDirection", lift.moveDirection, Directions);
    }
}

LifeEditorクラスのファイルはAssets/Editorの下に配置します。
これでLiftのmoveDirectionを設定する際、プルダウンでXYZを選択できるようになります。
他の変数も設定しておかないと、インスペクターに表示されなくなるので注意します。

20151202

動かすだけなら簡単ですが、3Dアクションでよくある「なめらかな動き」にするには工夫が必要らしいです。
位置を移動させる際にMathf.Sin()を使い、ラジアンを取って計算したりするそうですが・・・。
ここは知人に教えてもらって実装した部分なので、自分でもよく分かっていませんorz

また、CharacterMotorを使用している場合、MoveingPlatformの設定値が非常に重要です。
パラメータに関しては以下にまとめてあります。

【再編集】Unity開発メモまとめ「CharacterMotor」

まとめ

ということで、ちょっとしたステージギミックを作成してみました!
ただステージを進んで敵を倒すだけではつまらないので、適度に配置していきたいところです。

今日のイラスト

マリンパ、シルリスに続いてモグフィーです。
前から「暗い配色」と言われていましたが、描いてみると改めて実感します。
マフラーとか靴くらいは明るめの色にしてあげてもいいかもしれません。

mogffy11