Transform.positionの代入とCS1612エラーで色々考えた

UnityでC#のプログラムを書いていて、
ひと月に一回くらいウッカリ遭遇するエラーがあって、

transform.position.x = 10.0f;

とか書いたときに、

error CS1612: Cannot modify a value type return value of `CubeBehaviour.AAA.position'. Consider storing the value in a temporary variable

という。
正しく書くのは単にpositionのx,y,zは直接代入しないで、
new Vector3したものをpositionに入れればよい。

たまに忘れたころにエラー出てきて、おっとっと。みたいな。
僕がボケてるだけかもしれないけど、
ちょいちょい引っかかるので、
これなにがどうしてこうなってんの、どうにかなんないのっていうのを考えてみた話(たいしたオチはない)

エラー自体の意味するところは以下のものとなる。
Compiler Error CS1612


プロパティを介して代入しようとしてもダメだよ、プロパティは実体はメソッドだから。
ということである。

なにがプロパティなのかというとpositionなわけで、
定義を見てみると、

namespace UnityEngine
{
public class Transform : Component, IEnumerable
{
...
public Vector3 position { get; set; }

となっている。

プロパティでエラーになるっていうのがどういうことか、
もうちょっと分かりやすくpublic変数と比べてみると、

class AAA
{
public Vector3 position { get; set; }
}

class BBB
{
public Vector3 position;
}


void Start()
{
AAA a = new AAA();
a.position.x = 1.0f; // error: CS1612

BBB b = new BBB();
b.position.x = 1.0f;
}

AAA,BBBという2つのクラスに、
外から見て同じように扱えるpositionというものがあるが、
これは上のAAAのpositionへの代入だけエラーが出る。
プロパティだから。

ここで、じゃあTransformのpositionもpublicな変数にしちゃえば良いじゃない、
とか一瞬思ってしまったわけだが、
まあ、そうはなかなかいかないというものだと思う。

内部の詳細は当然分からないけど、
Transformの内部処理、エンジン側としてはオブジェクトの情報に変更があったということは知っておかなきゃいけない。
単純に座標の更新があったというだけすればいいわけじゃなくて
dirtyフラグを立てるであるとかして、
可視判定の更新をかけるかであるとか、
最適化で動いてないものは処理をスキップするとか
物理のほうでも位置の参照をしているであるとか、もしくは衝突判定とか、
親のTransformのほうでもAABBの更新するとかどうとか。とかとか。
妄想だけど、まあ色々やることはありそう。
なので実体はメソッドであるプロパティでないと都合は悪いのだろう。

なので解決方法としては、
"自分がしっかりする"、しかない。

まあ、それでもなにか少しできることであれば
拡張メソッドを用意しておいて、
そちらを使うことを習慣にしておく、
とかがひとまず打てる手かな、、と。

using UnityEngine;
using System.Collections;

public static class TransformExtension
{
    public static void SetPosition(this Transform self, float x, float y, float z)
    {
        self.position = new Vector3(x, y, z);
    }

    public static void SetPositionX(this Transform self, float x)
    {
        self.position = new Vector3(x, self.position.y, self.position.z);
    }

    public static void SetPositionY(this Transform self, float y)
    {
        self.position = new Vector3(self.position.x, y, self.position.z);
    }

    public static void SetPositionZ(this Transform self, float z)
    {
        self.position = new Vector3(self.position.x, self.position.y, z);
    }
}

例えばこんな風に。
座標のxだけ更新したい場合とかもちょっとスッキリ書けるようになるし。
transform.SetPositionX(10.0f);
とか。
メソッド名は微妙だが。

ゲームメカニクス おもしろくするためのゲームデザイン

ゲームメカニクス おもしろくするためのゲームデザイン (Professional Game Developerシリーズ)

ゲームメカニクス おもしろくするためのゲームデザイン (Professional Game Developerシリーズ)

買ったのはずいぶん前で、一度目を通していたと思うけど、
読み直した。
前回は途中でツールの説明になったあたりから後、飛ばし読んでしまったんだと思う。

サブタイトルで割とゲームデザイン全般に対する本のような、
アクションゲームはこんなのがあってー、STGはこうでー、
のような印象を受けるんじゃないかなって思うけど、
実際には、ゲーム内のリソースと内部経済・フィードバックループでの循環について
踏み込んだ内容になっていて、
著者のフレームワーク「Machinations」の紹介、ツールの紹介と、
それをつかったパターンについて書かれてる本、かな。
内部経済を表現できる適応範囲は広いので、
実際、パックマンのようなものから、RTSのようなものまで話題に上がってて、
ゲームデザインを考えるのに、
Machinationsを使うかどうかは置いておいても、
リソース・フィードバックループを分かっておくと良さそう。

ツールはFlash製なので、あんまり使い良くはない。
公式のwikiとかで動いてなかったりするし。
Android上で動作するものがあると、合間時間でもいじれてよいかも。

公式のページはこちらにあるので、とりあえず触るのにはここから。
wikiなんかへのリンクもあるし、
ツールのソースはgithubにあってそこへのリンクもある。
Machinations: Game Feedback Diagrams

フィードバックについての話題はMarc LeBlancさんのトークと合わせて紹介されていて、
スライドはここからアクセスできる。
The collected game design rants of Marc LeBlanc

GDCのサイトに、Machinationsに関してのセッションの動画があって動画がここから見れる。
www.gdcvault.com

日本語のスライドとしてはこちらを最初に見ると分かりやすいです。
Machinationの紹介

絵でわかる人工知能

絵でわかる人工知能 明日使いたくなるキーワード68 (サイエンス・アイ新書)

絵でわかる人工知能 明日使いたくなるキーワード68 (サイエンス・アイ新書)

CEDECで売られていたので購入。
ちょっとずつ読んだり、気になる章から読んだりしていたが、
人口知能に関わる各トピックごとに書かれているので、
どこから読んでも大丈夫。

森川さんのイラストも可愛くて、
何回も読み返せるし、
一回読んで終わりという本ではなくて、
気になったときに何回も読み直すような本かなって思う。

人口知能に関する広大なトピックを
俯瞰でみて広く全体をカバーしており、
この膨大な知識に基づいてかかれているのは素晴らしい。

著者である三宅さんがゲームAIのスペシャリストなので、
ゲームAIに関わるトピックも多く含まれており、
ゲーム開発者であれば読んでいて損はないはず。

カラーパレットを使ったSprite

f:id:kurihara-n:20160926233843g:plain

スプライトを用いたアイテムやキャラクターなど、
パレットを変えるだけで色替え出来れば、
カラーバリエーション分のカラーテクスチャを持つより、
容量節約できていいよねってことで
試してみた実装。
(ただし、ちょっと筋が悪いって思っている方法)

GitHub - kuriharaan/ColorPalette: Color palette experiment for sprites

やり方
パレットを作りたいテクスチャを選択し、
エディタスクリプトを実行すると、
テクスチャの各ピクセルを順に調べ、
カラーのリストを作り、
新しくソーステクスチャと同じサイズのアルファテクスチャ(Alpha8)を作って、
インデクスを格納していく。

描画には、アルファテクスチャをスプライトとして設定し、
パレットを参照するためのシェーダを持ったマテリアルを割り当てる。

Unity5.4からシェーダの配列パラメータの設定がスクリプトで出来るようになったらしい。
Unity - Scripting API: Material.SetColorArray
など。
スクリプトでは、このSetColorArrayを使ってテーブルを設定するようにしている。

シェーダではテクスチャのPixelからアルファ値をとり、
color 配列のインデクスとして使用するようにしている。

一応、これで望む挙動はしているが、
いくらか微妙な点がある。

・カラー情報を別途シリアライズしないといけない(今は初期値を決めうち)
・エディタでプレビューできない

逆に、よいところはスクリプトからカラーを設定すれば、
なんでもカラーを指定できるところ。

少し違ったやり方でパレットはテクスチャで持って、
スプライトのテクスチャに加えて
もう一段マテリアルに、パレットテクスチャを
指定する方法がある。
アセットストアにあるアセットのひとつに
OldSkool Palette Sprites
https://www.assetstore.unity3d.com/jp/#!/content/8649
というものが実はあって、
紹介動画を見ると、
パレットテクスチャを作る方法をとっている。
OldSkool Palette Sprites Tutorial - YouTube

こちらのほうが現実的な、運用に良い方法に思う。


追記。
仕組み上、アルファテクスチャはポイントサンプリングにする必要があるので、
そういうビジュアルで大丈夫な用途に限ったほうが良さそう。
これは紹介しているアセットも同じで。

Utility Based AI

Game AI Proを読んだので、いくつかの手法を実際に実装してみたくなり、
まず utility based AIを試してみる。

これは意思決定のための方法で、
いろんな場面に適用できる。
シムズみたいなゲームであれば、
AIエージェントが次になにをするかの判断、
コマンド選択式のRPGであればエネミーのアクションの選択に、
といった具合。

Game AI Pro では
www.gameaipro.com
Section 2
9. An Introduction to Utility Theory
に詳しく書かれている。

ゲーム世界から情報を統計・取得し、
0~1に正規化されたユーティリティ値にする。
それらを変換、組み合わせを行い選択肢となるアクションのスコアを決め、
一番高いものを選択する。
(もしくはウェイトにした行動を抽選したり、同種のアクションをバケツにいれて、とか。詳しくはGame AI proに)

階段 or エスカレータ どっちで行く?エージェント

Unity上で簡単な例を作ってみた。

f:id:kurihara-n:20160924161023g:plain

カプセル型のAIエージェントが階段に向かって歩いていき、
階段に近づいたときに、階段でいくか、エスカレータに乗るかの判断を行う。

階段、エスカレータそれぞれでエリア判定用のコリジョンを設定しており、
エリア内にすでにどれだけのエージェントが居るのかが判断のためのファクターとなる。

階段、エスカレータともにキャパシティの値を持っており、
現在のエージェント数をキャパシティで除算したのもが混雑度となり、
ユーティリティ値となる。
選択肢は階段かエスカレータどちらかということになるわけだが、
スコアとしては混雑していないほうがよいので
1 - [混雑度]
という変換をする。
正規化された値なのでこういう変換が簡単。
あと、このままだとリニア曲線になるので、値を二乗して二次関数になるように。

こんな感じ。

        float crowdedness = (float)statictics.escalaterArea.NumberOfAgents / (float)statictics.escalaterCapacity;
        float score = 1.0f - Mathf.Min(crowdedness, 1.0f);
        return score * score;

まずはこれで階段、エスカレータでスコアを出し、
比較して意思決定を行う。


これだけだと単純すぎるので、別のユーティリティを加味するようにしてみる。

階段は長くなるほど、上るのは疲れてしんどいもの。
なので、階段の長さをファクターとし、
階段をアクションとする場合のスコアに掛け合わせる。

        float crowdedness = (float)statictics.stairsArea.NumberOfAgents / (float)statictics.stairsCapacity;
        float hardships = Mathf.Min(1.0f, statictics.stairsLength / statictics.stairsLengthMax );
        float score = 1.0f - Mathf.Min(crowdedness, 1.0f);
        return ((score * score) + (1.0f - hardships)) * 0.5f;

階段の最大長として想定している値で、階段の長さを除算し、
ネガティブな値に変換(1から引く)、
もともとのスコアと、平均を取るようにする。
これで、階段が長いほどスコアが下がるようになります。

階段が長いと判定されるような値に設定しておくと次のようになる。


f:id:kurihara-n:20160924233805g:plain

ほとんどのケースで、エージェントがエスカレータを選ぶようになる。

実際には、階段の長さに応じて判定のための値を設定するようにし、
エージェントに疲労パラメータを追加してもよさそう。
こんなふうにパラメータを後から足せるのが非常に良い。


Game AI Proにも記述があるが、
ユーティリティ計算のためにはエディタを作るとよいようで、
実際、すべて正規化された値なので、計算方法の組み合わせで表現ができ
データドリブンに出来るので良さそうである。
プロジェクトを進めるにあたって、
ある程度Utility basedに基づいてAIを構築していくのであれば、
ノードエディタもセットで作ると良さそう。


今回試したプロジェクト
GitHub - kuriharaan/UtilityBasedAI: Experimental project for utility based AI

無料で読める "Game AI Pro" がとても良い

Game AI Proという、ゲームAIについての記事をまとめた本があります。
Game Programming Gemsのように技術について書かれたトピックを、
たくさん集めた本、それのゲームAIについて特化したバージョン。

Game AI Pro, Game AI Pro2という2冊の本が現在刊行されているが、
最初のGame AI Proは公式サイトですべての記事が公開されています。

www.gameaipro.com

ゲームAIは最近、テクニックが細分化され高度化されているおり、
ゲームプログラマとしては、ある程度把握しておくのは良いこと。

最近だと開発会社によっては専門職としてゲームAIプログラマーを採用していることもあり、
AIに習熟していけばエキスパートを目指すのもありだし、
そうでないオールマイティなゲームプログラマとしても、
スキルセットに持っておくのはプラスになるでしょう。

公開されているのは英語版になり、
まだ日本語には翻訳されてませんが、
それでも読む価値はあると思います。
とても有用な本なので、是非日本語化されて欲しいとは思いますが。

今回、さっと読んでみて、
とばし読んだところもあるけど、
各章の概要をまとめておこうと思います。
紹介程度に。

Section 1: General Wisdom

1. What is Game AI?

概要。Game AIの定義とは。

2. Informing Game AI through the Study of Neurology

神経学の入門。

3. Advanced Randomness Techniques for Game AI: Gaussian Randomness, Filtered Randomness, and Perlin Noise

シンプルなランダムではない3つの特徴のある乱数の紹介。

Section 2: Architecture

4. Behavior Selection Algorithms: An Overview

アルゴリズムの選択について。 FSM/HFSM/behaviour tree/GOAP
Hierarchical Task Networks(HTN)

5. Structural Architecture -- Common Tricks of the Trade

アーキテクチャの紹介。hierarchy/option stacks/black board/Modularity

6. The Behavior Tree Starter Kit

BTについて概要と実装。

7. Real-World Behavior Trees in Script

Kingdoms of Amalur: Reckoningというゲームでの実例。
ビヘイビアの実装をLuaスクリプトで書くことによるイテレーションの高速化。

8. Simulating Behavior Trees: A Behavior Tree / Planner Hybrid Approach

ビヘイビアツリーとプランニングのハイブリッド実装。

9. An Introduction to Utility Theory

ユーティリティ理論・ユーティリティベーストAIのイントロダクション。

10. Building Utility Decisions into Your Existing Behavior Tree

ユーティリティのBTへの組みこみ。

11. Reactivity and Deliberation in Decision Making Systems

Reactivity(反射、反応)とdeliberation(熟考)について、どのデザインが効果的か、どう落とし穴を回避するか。
意思決定システムにはReactivityとDeliberationが必要。

12. Exploring HTN Planners through Example

hierarchical task networks(HTN)について。

13. Hierarchical Plan-Space Planning for Multi-Unit Combat Maneuvers

Multi agent におけるプランニングは、協調させる必要があり、ワールドステートが膨大になるためGOAP,HTNが適用しにくい。
全てのプランを含むplan-spaceで調べるメリットについて。

14. Phenomenal AI Level-of-Detail Control with the LOD Trader

LODについて。距離だけでは微妙。

15. Runtime Compiled C++ for Rapid AI Development

RCC++ runtime c++コンパイルについて。

16. Plumbing the Forbidden Depths: Scripting and AI

スクリプティングについて。

Section 3: Movement and Pathfinding

17. Pathfinding Architecture Optimizations

A*より最適化の余地がある。事前計算など。

18. Choosing a Search Space Representation

探索のための世界表現の選択。
Grid / Weigh-pointed graph / Navigation Meshes

19. Creating High-Order Navigation Meshes through Iterative Wavefront Edge Expansions

ナビゲーションメッシュの生成アルゴリズムについて。

20. Precomputed Pathfinding for Large and Detailed Worlds on MMO Servers

MMO(FF14)での事前計算ナビゲーションの用例。

21. Techniques for Formation Movement using Steering Circles

Steering circleを用いた隊列移動。

22. Collision Avoidance for Pre-Planned Locomotion

Hitman:Absolutionでの、ADL(Animation Driven Locomotion)とコリジョン回避の組み合わせ方法。
Reciprocal velocity obstacle techniques (RVO)

23. Crowd Pathfinding and Steering using Flow Field Tiles

FlowFieldを利用した群衆の移動。

24. Efficient Crowd Simulation for Mobile Games

FieldRunner2におけるFlowField。モバイル最適化。

25. Animation-Driven Locomotion with Locomotion Planning

ADL を状況に合わせてプランにする。
Bulletstormでの例。

Section 4: Strategy and Tactics

26. Tactical Position Selection: An Architecture and Query Language

Tactical Position Selection (TPS)
CryEngine, Crysis2。
DSLによる非同期抽出,重みつけ

27. Tactical Pathfinding on a NavMesh

最短経路よりも、危険性などを考慮した良い経路をナビメッシュで見つける方法。

28. Beyond the Kung-Fu Circle: A Flexible System for Managing NPC Attacks

Kingdoms of Amalur: Reckoningでの例。
よくある多数のエネミーとの戦闘で通常1人ずつ攻撃してくるようなケース(Kung-Fu Circle)で、
ダイナミックまたは難易度に応じて攻撃してくる人数を変動させる。
ベルギアンAIの紹介。

29. Hierarchical AI for Multiplayer Bots in Killzone 3

Killzone3のBots
3つのレイヤーからなるアーキテクチャ
レイヤーは下のレイヤーを制御し、下のレイヤーは情報を返す。

30. Using Neural Networks to Control Agent Threat Response

supreme commander 2にて
ニューラルネットワーク使ったら非常に効果的だった話。

Section 5: Agent Awareness and Knowledge Representation

31. Crytek's Target Tracks Perception System

CryEngineのTarget Tracks Perception System (TTPS).
主人公の居場所などをAIに渡してしまうと不自然に感じられてしまう。

32. How to Catch a Ninja: NPC Awareness in a 2D Stealth Platformer

Mark of the NinjaのAI実装例。
interest sourceを使ってオブジェクト・イベントを大量に作成する作業の軽減や、
roleをエージェントに与えて自然なグループの反応の実現について。

33. Asking the Environment Smart Questions

Bulletstormの例。
低CPUコストで直観的なシステム。Environment Tactical Querying (ETQ)

34. A Simple and Robust Knowledge Representation System

EverSkyの例。
Knowledgeについて、blackboardの改善。

35. A Simple and Practical Social Dynamics System

パーティでのハグなど言葉を用いないコミュニケーションにより世界をより納得感のあるものに。
Gaze control(凝視)/Proxemic Control(位置、距離)/Posture(姿勢)/Gesture

36. Breathing Life into Your Background Characters

背景NPCについて。Schedule system。

37. Alibi Generation: Fooling All the Players All the Time

町を歩くNPCをより高度なものにするために意味をつける。
どういう背景を持っていて、なにをするのか。

Section 6: Racing

38. An Architecture Overview for AI in Racing Games

レースゲームのためのアーキテクチャ。4つのレイヤーと一つのサブシステム(コリジョン回避)

39. Representing and Driving a Race Track for AI Controlled Vehicles

Track(コース)について。

40. Racing Vehicle Control Systems using PID Controllers

PIDコントローラについて。

41. The Heat Vision System for Racing AI: A Novel Way to Determine Optimal Track Positioning

ひとつの車への反応を記述するのはできるが、同時に一台となってしまう。
混雑した状況での挙動のためにHeat visionというシステム。

42. A Rubber-Banding System for Gameplay and Race Management

Rubber-banding。
プレイヤーの前に離れているときはスローダウン、
後ろにいるときはブーストしてプレイヤーの近くに来るようにする。

Section 7: Odds and Ends

43. An Architecture for Character-Rich Social Simulation

Prom Weekで使われた Comme il Faut (CiF) social simulation architecture について。

44. A Control-Based Architecture for Animal Behavior

World of Zooの例。
Control-Basedアーキテクチャ

45. Introduction to GPGPU for AI

GPGPUの話。OpenCL

46. Creating Dynamic Soundscapes using an Artificial Sound Designer

Artificail Sound Designer。
イベントを登録してDBを更新して適切な音を鳴らしてくれる。

47. Tips and Tricks for a Robust Third-Person Camera System

Third-Person viewにおけるカメラについて。

48. Implementing N-grams for Player Prediction, Procedural Generation, and Stylized AI

N-gramsの推測をゲームへの適用する。



以上、簡単なコメントを読んだメモから補足して、
書いてます。

僕自身は実はGame AI Proをkindle版を購入してましたが、
そのあとpdfの公開に気付きました。

次はGame AI Pro2も読みたいところです。

Unity5.4 で Object.Instantiate()に親が指定できる

タイトルままですが、
ふとintellisenseが見慣れないサジェスチョンしてきたので気づきました。

docs.unity3d.com

引数にparentとなるtransformを指定できます。
生成時に他のTransformの子として生成できます。

これは便利、というかいままで無くて不便なので、
拡張メソッドでInstantiateAsChild(this Transform parent) みたいなものを作ってました。


Release noteにも

unity3d.com

Core: Object.Instantiate now takes a optional Transform parent parameter.

ちゃんとありましたね。

    [SerializeField]
    GameObject childPrefab;


    void Start ()
    {
        var parent = this.transform;
        Instantiate(childPrefab, Vector3.zero, Quaternion.identity, parent);
    }

こんな感じで。
オブジェクトに追加しておくと、

f:id:kurihara-n:20160813011402p:plain

このように呼び出したものの子になる。