雑学豚の巣穴

盛大なメモ帳

新作ゲームの歓喜とその弊害 あつ森

あつまれ どうぶつの森ずっとやってましたやんか

 

岩をたたいて素材でてる間は道具の耐久値は減るけど破壊はされず耐久値1は残り続けるとか

スコップで何もない場所を掘るだけでは耐久値は減らないとか

木の成長は周囲8マスに物がなく邪魔が無い事、自分を中心に9×9のマスに自分を含め12本までである事(13本以上あると育たない)とか

花だったり掘れないレンガや岩の地面が周囲のマスにあっても育つとか

こっち目線の仕様把握プレイをしておりました。

 

地面に何か埋めとけばそこに果実が落ちないから果実の落下地点を操作できるとか

家具だと間違えて回収しちゃうからその役目を花にしとけば島の評価も上がるとか

貝とボトルメールとカモメは海岸線に沸くからそこを家具や柵で埋めると好きなところに密集させられるとか

効率目線のプレイもしておりました。

 

生活系ゲームじゃなくて生産ゲー蒐集ゲーとして遊んでるがすぎる。

 

 

 

ここからゲームファンらしい話

任天堂らしいプレイヤーの気づきと誘導、プレイヤーが自分で成果を出したぞ!と思わせる配置が今回もうまいなぁと。

 

・余談

スーパーマリオブラザーズの1-1は最後に作られたという話

全てのステージを作ったプロが考えに考えて作った初心者ステージ

それが1-1である。

 

このステージではダッシュジャンプが必要なブロックや土管の高さはなく落下死する幅も2マスしかない。

最初に落下する場所には「敵の居ないない十分な平面」と「直前に見えない?ブロック」が配置されている。

 

平面により穴の位置の確認をしやすくし

例え落下するタイミング(飛び越えるには早い位置)でジャンプしてしまった場合、?ブロックを叩いてしまい地面に着地する。

そこで練習させて次が本番の穴越えなのだが、

その穴の直前で落ちても大丈夫な(ほぼ)同じ地形を飛ばせてから、落ちたら死ぬ地形を飛ばせる。

 

他にも

上から歩いて落ちてきて下から叩けと言わんばかりの敵にそのレンガブロックを叩いたと思ったら出てくる隠しブロックなど

任天堂らしいプレイヤーの気づきと誘導が発揮された1-1なのである。

 

閑話休題

あつまれ どうぶつの森ではどうだったか

1日目はやれること制限して遊び方や目的を「これは練習です」と言わんばかりにやらされる。

見るからに「練習です」「遊び方です」を提示されているのでらしくはないが、最近のゲームというのはこういうことが必要なんだという感覚もある。

 

しかし任天堂らしいなを感じたのが急に来る「てっこうせき30個」である。

この「練習です」の段階を続けていると数日で素材を集めろという練習が始まる。

木材や果実、そして石系素材のてっこうせき。

自分の島だけでは1日の入手数が限られており数日でここにたどり着いてしまうと全く足りない。

素材が出ないなら目的のために進められることも無くなり、ついさっき解放された「マイレージりょこうけん」でも使ってみるかという考えになる。

 

離島に行くためのアイテム…

その離島でなんと新しく素材が取れるではないか!

てっこうせきが欲しいならだらだらとまた数日かけて集めるか「マイレージりょこうけん」で行った離島で入手するしかない。

 

マイレージとは実績みたいなもので実績達成でポイントが得られる、

マイレージ+という何度でも達成できるクエストみたいなものでもポイントが得られる。

ポイントを使って生成する小ダンジョンで素材が取れる…という形である。

素材が足りないからマイレージポイントをためるようにプレイヤーに気づかせる。

 

 

 

明日まで待てない!素材を集めるぞ!や離島ってどんなのだろう1度行ってみよう

という形で離島に向かうと

そこには見知らぬどうぶつ(住人)が居る

そして、てっこうせき30集めた後に言われるのが「住人を島に移住させよう」

という内容。

ここでも先にちょっと見せて次に問題を出す

任天堂らしいプレイヤーの気づきと誘導、プレイヤーが自分で成果を出したぞ!と思わせる配置…

 

今回もやるなぁ

例えば残存HPと画面の表示の関係

数字表記とHPバーがあってダメージ受けたら減らしたいよね

 

内部では数値として持っていて、

画面に表示する数字表記はテキストで、

画面に表示するHPバーの長さにも反映させなきゃいけなくて、

 

 

ダメージと追加効果持った火球がプレイヤーに当たったらダメージ関数に数値を渡して

渡ってきた数値から処理はプレイヤーで行うとして

HPはプレイヤーが持ってるからいいとしても

「画面に表示されてるもの」という物体をプレイヤーが触りに行くのはちょっとなーだし

画面が常にプレイヤーのHPを監視して表示を変えるのもなんだかなーだし

欲しいのはHPの値が変わった瞬間だけなんだけど

HPが変わったよ!と教える方法は無いものか

 

変わった?変わってない。変わった?変わってない。を毎フレームごとやるのが嫌なんじゃ…

 

System.ComponentModelにINotifyPropertyChangedってのがありますやんか

Property…Changed…可能性がありそうだけど難しそう…難しそうじゃない?

やりたいのは値変えられたら設定された関数呼びたいだけなんや

 

そんなあなたにSystem直下のやべーやつ

System.Actionである

キーワードはActionとラムダ式だ!


    //「画面に表示されてるもの」用のスクリプト
    
    public void hpChange()
    {
        hpText.text = 
        PlayingManager.instance.hp.ToString() 
        + " / "
        + PlayingManager.instance.max_hp.ToString();
    }

 

単純に言えばHP変更されたらこれが呼ばれたい訳よ

でもHPはPlayingManagerというゲームの基礎の数値持ってる所にあって変更されたかどうかは「画面に表示されてるもの」側で確認が難しいと。

そこで出てくるのがActionクラス

 

まず値変更監視用のクラスを作り、そのクラスに値とAction変数を持っておきます

ジェネリッククラスにより好きな型を値にしとけば汎用性高いけど別に整数型でもいいです


public class 監視クラス<T>
{
    private T val;

    public T Value
    {
        get
        {
            return val;
        }
        set
        {
            val = value;
            //変更されたよ(val); //真に変更されたよルート行くなら値設定前にvalとvalue比較しよね
        }
    }
    public Action<T> 変更後に行きたいやつ; //どうせsetで値貰えてるんだから引数ありでやろう
}

 

このクラスを使ってPlayingManagerでhpを宣言・管理しますよ

 

「画面に表示されてるもの」側でPlayingManagerのHPにある変更後に行きたいやつに関数を設定します。こういうの名前適当につけがち。

PlayingManager.instance.hp.変更後に行きたいやつ += value => hpChange();

 

 

最後に監視クラスに変更されたよを実装

    private void 変更されたよ(T val)
    {
        var 行きたいの = 変更後に行きたいやつ;
        if (行きたいの == null)
        {
            return;
        }
        行きたいの(val);
    }

 

 

こんな感じでHP変更された管理とテキスト変更連動!

チカレタ…

今回真面目すぎひん?

 

プレイヤーも敵もキャラクター

落下判定のタイルマップを作ってここに接触したら死亡にするぞー

f:id:rrroo:20200331044549p:plain

テストマップの落下判定

 

接触したら死亡だし死亡処理は前作ったからいいとして…

 

1.プレイヤーの接触イベントで落下判定とぶつかったら自分の死亡処理

2.落下判定の接触イベントでプレイヤーがぶつかってきたらプレイヤーの死亡処理を呼ぶ

 

どっちを選ぶべきか?

実際は針に当たったロックマンみたいなものなんだけどなー

 

例えば魔法攻撃があって鈍足になる効果の弾に当たったら

1.プレイヤーの接触イベントで魔法攻撃とぶつかったら魔法の種類ごとの処理をプレイヤーが判断して変える

2.魔法攻撃の接触イベントでプレイヤーがぶつかってきたらプレイヤーの状態異常や効果時間の設定を変える

 

こう考えると後者な気がする。

敵:くらえファイヤー!

火:

自:わーぶつかってきたこの火はファイヤーだから20%でやけどだー失敗したけどー

敵:くらえ火遁!

火:

自:わーぶつかってきたこの火は火遁だから50%でやけどだー成功したー

 

敵:くらえファイヤー!

火:プレイヤーに当たった!20%でやけどだ!

自:わー失敗したかーなんともないぜー

敵:くらえ火遁!

火:プレイヤーに当たった!50%でやけどだ!

自:わー成功したからやけど受けてるー

 

後者のがそれっぽい…それっぽくない?

バネ処理でも似たような事した気がする

(あーでも魔法反射だったらどうなるんだろう)

(ダメージだけ届けてレジスト判定はプレイヤー側が正解か?)

(やけど耐性装備してたらって判定はプレイヤーですべきだしな)

今回はどっちの処理で判定を取得するかという話で。

 

とりあえず今回は死亡判定側がぶつかったものが何かを見る

プレイヤーだったら死亡処理を呼ぶ。

CompareTagでプレイヤータグと一致したら

Collider2D.gameObjectの

.GetComponent<コンポネート名>()とってきて

自作.Dead();を実行

コンポネート名はプレイヤーにつけてるスクリプト

 

プレイヤーの落下死亡はできたけど

今のままだと敵キャラが落下してもずっと生き残っちゃう。

もうゴミオブジェクトなのに移動計算続けるのもなー…

敵も当たったら死亡処理だっ

 

タグ見てコンポネート名変えた処理に変えてもいいんだろうけど

 その時るーに電流走る

コンポネート名はクラス名だから…Dead関数を親クラスに持っておいて親クラスをプレイヤーや敵が継承すればいいのでは?

 

■クッソわからない継承の話

よく人間に例えられてる

人間は全員 名前と年齢を持っている

しかし、人によってはマラソン選手だったら他人には不要な最速タイムだったりを用意しなくてはいけなくなる。

そういう様々な人に対して1つ1つ専用の設定を作る必要があるとしても

100あれば管理の仕方は「人間が100人」のが楽だし

「君の名前は?」は人間に共通して聞ける内容だ。

それはマラソン選手に対しての質問ではなくて、人間に対しての質問だから。

そういう概念を大幅にとらえたものを用意してからそれを内包した詳細な概念を作る

これが継承である。

例にならえば人間を継承したマラソン選手という概念。

ラソン選手は名前と年齢を人間として最初から持ってるので最速タイムを追加すればいい。

 

話は戻って

 

落下判定に当たったプレイヤーと敵は死亡処理を行いたい。

楽な話さ

プレイヤーも敵もキャラクター.Dead();を継承してキャラクターとして問えばいい

死亡関数開始して下さい。

 

「unityの基礎クラスMonoBehaviour」を継承した「CharacterData」を継承した「Enemy」ってクラスを作る

 


public class CharacterData : MonoBehaviour
を作って
    public void Dead() { } //死亡関数を持ってることを宣言だけする

public class Enemy : CharacterData
継承したら

    new private void Dead()
    {
        c2D.enabled = false; //当たり判定消失とか
        isDead = true; //死亡フラグ立てたりとか
    }

中身を作って
落下判定のプログラム側で

    //当たり判定イベント
    private void OnTriggerEnter2D(Collider2D c2D)
    {
        if (c2D.CompareTag("enemyTag") ||
            c2D.CompareTag("playerTag"))
        {
            //即死に当たり死亡(落下)
            
            //CharacterDataとしてDead関数を呼ぶ
            c2D.gameObject.GetComponent<CharacterData>().Dead();
        }
    }

 

騎士である前に女だ!とか女である前に騎士だ!とか女騎士と姫の百合は大変良いものだとか。百合の間に割り込み隊長だとか。

 

待てあわてるな。まずは逆の立場から考えるんだ。

面倒な処理が必要になる→そもそもそんな処理など不要なのでは

 

やっぱり自分の当たり判定の周囲にTriggerになる当たり判定をつけてみては?

何個もつけなくていい1つだけ

プレイヤーよりひとまわり大きいのを用意してTriggerならコライダー取ってこれるから位置やTagで判断もできるだろうしなぁ

という心変わり

 

基本的に人の考えは逆の結果になることもある

そこを考慮しておくのも大事なのだ。

人員削減しようと希望退職者を募ったら他でもやっていける有能が消えてすがりつく無能が残るのさ

田舎が人口分散させようと新居住者の支援制度を用意しても同じくらいの田舎から優遇制度のある田舎へスライドするだけなのさ

檻は逃がさないための囲いよりも外の危険に会わせないため

 

逃げ出した豚ちゃんは狼さんに食べられる

逃げ出した檻の中が一番安全とは知らずに

自分が絶対に正しいと思って進んだ先を疑わずに

 

 

 

「これ仕様変更しようという案も間違いなのでは?」

「そもそも開発中盤の大型仕様変更なのでは?」

 

葛藤の中で初心に返ろうとするとすでにそこは霧の中で

 

「心変わりを起こすほどの衝撃的場面に出会ったはずなんだけど何があったんだっけ」

と、ポエムじみた事を吐き出すほどの何かがあったはずなのに忘れているのだ

 

冷蔵庫開けたのに何取り出すんだっけ現象あるよねー

ぽえっと!

 

敵が敵とぶつかってお互い反転する処理はコライダーの接触処理でやれたのに

目の前が壁かどうかの処理は接触の部分で処理できてないのが気持ち悪かったんだと思う

それは「ぶつかったかどうか」という処理なのに別の担当は論理的じゃない。一か所にまとめるべきだしそういう組み立てを行うのが正しいはずという考えがあって。

それがやれていない今は正しくない組み立てをしている。

 

面倒な処理が必要な所がまた「正しくないのでは?」という部分として表れていて、正しい処理とはぶつかった判定用のオブジェクトを用意することなのではないかという懸念点がここにある訳で…。

一度ここで変な形の成功体験を得てしまうと次も「地面との接触?そんなものRaycastやLinecast、OverlapAreaを使えば?」となってしまう

でもオブジェクトをごちゃごちゃくっつけるのもなーという当初の思想もある

敵が敵とぶつかってお互い反転する処理自体をコライダーの接触処理じゃなくて自前の判定ですればいいのでは…?せっかくのイベント全部無視するのか…!

 

ぐるぐるして出た結論は

 

そんなもん好きにしとけ

 

第一話 くたばれ!ボックスコライダー!

 

敵は四角だしボックスコライダーで作ってー

引っかかり対策でエッジの半径って設定つけて角を曲線にしてー

X方向に速度を与えてー

よしっ歩かせることに成功!

プレイヤーが持ってる攻撃判定に当たったら死ぬ設定も有効!

 

ここで次の段階、敵キャラは壁にぶつかったらUターンさせたいよねである。

 

壁も設定上は地面だしなー…落下して着地も地面との衝突だから地面にぶつかったら反転って訳にもいかないし

そもそもOnCollisionEnter2Dは地面に接したまま地面とぶつかっても発生しないし

プレイヤーに使った目の前のレイヤーが地面かどうか判定使うか

 

目の前のレイヤーが地面だったら移動X方向を乗算で-1するだけの設定でとりあえずテスト

…壁にぶつかってるのに反転しないなー

壁判定取得してないのかな?

LayerMask.NameToLayer("Ground")が悪いのかな?

そもそもタイルマップのレイヤーの設定どうなってるんや

え?256?おかしない?でも正常に動作してる…なんでや

ま、まぁ精査は後として敵キャラにも同じ設定して動かしてみよう。

 

設定同じでもダメか―

何が悪いのかなー目の前って設定があかんのかな

c2D.bounds.centerにc2D.bounds.extents.xと前方定数を移動方向(1か-1)かけたの足したのが前方でー

その座標どこだろか…?

ちょっと見てみるかー

正しそうに見えるけどなー

…なんか座標誤差でずれてない?

前方短くない?

 

『引っかかり対策でエッジの半径って設定つけて角を曲線にしてー』

『エッジの半径って設定つけて角を曲線にしてー』

『エッジの半径って設定』

 

ちょっと待って?

まさかコライダーのサイズにこれ含まれてないの…?

当たり判定の大きさ広げる設定なのにサイズに含まれないの…?

しかもこれ?直線使うコライダーに用意された設定だから親クラスのCollider2Dには無い設定なの…?

共通関数にedgeRadius足すのちょっと変えなきゃなー

 

f:id:rrroo:20200330041742g:plain

一つ壁に当たったら方向転換し二つ敵に当たったら方向転換し

 

死亡判定と踏みつけ判定

死亡判定はどうするの?
敵に当たったらHP減らしたり死亡関数呼び出せばいい
HP減るなら連続ヒット回避の無敵時間処理と点滅エフェクト処理とか用意しなきゃいけないよね
無敵は無敵アイテム関係もあるし作ってもいいのでは?
 
落下は?
プレイヤーのyの位置がこれ未満なら死亡よりも、落下位置って即死の当たり判定を用意するのはどうだろう。
落下位置すり抜けされたら死ねなくなるけど…?
 
そういう課題が地味に積まれていく。
なんだかんだ死亡はプレイヤーに攻撃が当たったらというわかりやすさがあるけど。
 
面倒なのは攻撃側である。
『ジャンプ中に下半身で接触していれば踏み攻撃』
これが初代マリオの仕様を端的に表したもので、上昇中に横から当たっても踏めるのだ。
リアルタイムアタックとかでは空中の敵キャラを踏んで大ジャンプ「今あれ踏んでんの!?」ってやつをよく見る事ができる
いやだって横から当たってんじゃん
 
さて、自作のアクションゲームで踏んで攻撃の判定をどう致すか?
プレイヤーが踏んでるんだからプレイヤーの当たり判定と敵の当たり判定の接触時に攻撃の可否処理をやれば?も一理あるどころか大賛成なんだけど
踏むと表現はされるけどプレイヤーとの接触ではなくてさー攻撃判定に当たったから倒せるってすべきじゃない?攻撃は攻撃領域を持ってしかるべきじゃない?ってのも分かりやすい理だなと思う訳で…
地面判定の確認に足元に変なのくっつけるのは反対だけど、
足を武器にした攻撃なら足に攻撃判定くっつけるのは賛成です。
 
攻撃する瞬間に攻撃範囲のコライダーを有効にするを触ればいいのでは?
enabledの値を変えるか
setActive関数を使うか
で対応できそうsetActiveだと関連してる子も消せるけど処理が重いっぽい?なら単純なenabledでいいかなー
 

敵を踏みつけたとかバネに飛び乗ったとか

敵を剣で切るとか拳で殴るとかのゲームならまだ踏みつけジャンプのプログラムはいいけど

ジャンプ台はアクションでは常套手段だよねー

こういうのはプレイヤー側にこれに乗ったら何するって設定じゃなくて乗っかられた側に「俺に乗ったらこれくらい跳ねるんすよwww」って設定のがいい

コライダーのマテリアル的な考え。

 

やりたいことは

ジャンプ中に再ジャンプ容認でしかも高度はまちまち

ということがやりたい訳で。

あれ?これ今の「地面に居たときだけジャンプ!」の仕様とはだいぶ違うし結構変えなきゃなぁ

 

どうすれば実現できるかなーと悩んだところ

 

目標高度ってデータを持って飛んでるフラグ立ってる時はそこに向かえばいい

小ジャン大ジャンの時もこの目標の数値を設定すればいいだけでは?

ジャンプ開始関数を作る。目標のジャンプ高度を引数で。

 

ジャンプ開始関数例

public void StartJump(float height)
{
	//飛ぶ処理

	//飛び初めの位置
	oldYPosition = c2D.bounds.center.y;
	isJump = true;

	//ここで設定するのが正しいかわかんない不具合出たら考えよう
	junpButtonTime = 0.0f;//小ジャンとかのボタン押してる判定時間
	junpFirstButtonFlg = true;//ジャンプ開始からの押しっぱフラグ

	//目標の高さ
	targetHeight = height;
}

 

 

プレイヤーの設定では地面の上に居る時だけジャンプ開始関数を呼べるようにしといて、

この関数自体は外からも呼べるようにしとこう。

こうすればバネ側で乗られた状態でジャンプすれば引数に高さを渡してハイジャンプができる…はず。

ダメだったら変えればいいやが設計書なしでだらだらプログラムの真骨頂!

「ソースが設計書なんで」

 

f:id:rrroo:20200326030304g:plain

(個性的な)バネに乗ったままジャンプボタン押してたら超飛ぶぞー

 

こうなると欲が出る重力反転してたら『上』はyがマイナスの方向だ

transformのlocalScale.yがマイナスだったらこの足元判定から高度から反転して設定しなくては…と。

 

プレイヤーは単純な飛ぶ処理!とか死ぬ処理!だけを持って数値や操作は影響する空間側で設定するのがいいなって。