Yaminabe

らくがきぶろぐ

てすと:Yaminabeちゃんは生まれたばかりのブログです みんなで仲よく使ってね

カテゴリ: Unity3d



前回更新から間が開いてしまい 申し訳ないです。 シェーダーか背景と予告したのですが

Sendmessage()メソッドの検索が多かったのでちょっと寄り道して データ最適化の話をします

コードを綺麗に見せるスクリプトを記事に埋め込むと余裕の文字数オーバーしてしまうので見づらいのは
勘弁して下さい。 色とかこりたいんですけど 記事のデータ量制限がきびしくてね シンプルにならざる。





■Sendmessage()で複数の値を送信する方法

Sendmessage ()で複数の値を送信するには 配列、クラスなどを利用します



●配列を使用する場合

●送信側

var goTo : Vector3;
var arr = new Array();

arr.Push (goTo);
arr.Push (true);
other.SendMessage ("GoTo", arr);



●受信側


function GoTo(arr){
var dest : Vector3;
var input : boolean;

dest = arr[0];
input = arr[1];
}




●クラスを使用する場合

●クラスの宣言

public class DamageParams
{
public var damage : float = 0.0;
public var type : int = 0;
public var source : NetworkPlayer = Network.player;

//コンストラクタここから
function damageParams( damage : float, type : int, source : NetworkPlayer)
{ this.damage = damage; this.type = type; this.source = source; }
}


●送信側

function dealDamage( other : GameObject )
{ other.SendMessage( "takeDamage", new DamagePrams(10, 2, Network.player) ); }


●受信側

function takeDamage( params : DamageParams )
{
if(params.type != 3)
health -= params.damage;
}


参照:http://answers.unity3d.com/questions/170384/multiple-paramaters-with-send-message.html


●NotificationCenter(通知センター)
Unity公式のフォーラムWiki に記述がありますがNotificationCenter直訳だと通知センターを使用した

SendMessageを効率よくオブジェクト間で送信するスクリプトがあります。 ※リンク先を参照してください

http://wiki.unity3d.com/index.php?title=NotificationCenter
http://wiki.unity3d.com/index.php/NotificationCenter3_5
http://wiki.unity3d.com/index.php?title=NotificationCenterGenerics


これは あらかじめ通信させたいGameObjectを配列に登録しておけば効率よくデータ送信できるというスクリプトです。

カスタマイズされて複数のタイプが存在しますが ジェネリック型Hashテーブルを使用したタイプが扱いに便利そうです




●プログラマ以外の方にもわかるように一応解説しますよ

スクリプトは シングルトンパターン で記述されています。

シングルトンというのは複雑な処理を行うメソッドでシーンの中に常に1つだけ存在させたいスクリプトを扱う場合に使用される記述法です。



シングルトンパターンの例)

using UnityEngine;
public class MySingleton : MonoBehaviour
{
private static MySingleton instance;
public static MySingleton Instance
{
get
{
if (instance == null)
//アプリケーション起動時にこのシングルトンクラス(”My・・”)が存在しなければGameObjectを生成してコンポーネント追加
{
instance = new GameObject ("MySingleton").AddComponent ();
}
return instance;
}
}
public void OnApplicationQuit () // アプリケーションの終了時にこのコンポーネントを削除
{
instance = null;
}
}


参照:http://wiki.unity3d.com/index.php/Singleton

シングルトンパターンのスクリプトはシーン中に置いておく必要はなくてアプリケーション(ゲーム)起動時に自動でインスタンスが生成されて アプリケーションの終了時に破棄されます。

リンク先にカスタマイズされたされたシングルトンパターンの例がありますので参考にしてください

※例はC#ですがUnityScriptに書き換えるときに注意するのはアクセサー(get / set)です。C#からUnityScriptへ移植するときに悩んだ方もいるのではないでしょうか。

アクセサはC#固有のものでJavaやC++にはありませんが UnityScriptではメソッドが用意されています


例) UnityScriptの場合

class JSClass extends MonoBehaviour
{
var x: String = '';
function get X() : String { return x; }
function set X(value : String) { x = value; }
}




● set { setアクセサー(setter ) 値の変更時の処理を書く value という名前の変数に代入された値が格納される。 }
● get { getアクセサー (getter ) 値の取得時の処理を書く メソッドの場合と同様に、値はreturnキーワードを用いて返す }


例のようにfunctionをつけてクラスの内部で使用します。

Nofitication Centerに関して本家フォーラムのスレッドでStatic変数を使用して値をやり取りするのとどちらがいいのかという議論がなされていますが static変数はメモリ常駐してしまうのでメモリの管理が必要になる点が問題

メモリに関しては使わない変数にはnullを代入しておくなどオーソドックスな手法で対応出来ますが 結論としては

ケースバイケースかなw Nofitication Centerを使用したサンプルはいままで一例(デジタルチューターのチュートリアル)しか見たことがなかったりなので ほんとにこれ使われてるのかしらという疑問もあったり。。



と ここまでSendMessage ()の解説でした が UnityのコミュニティWiki のGeneral Performance Tipsによると

http://wiki.unity3d.com/index.php/General_Performance_Tips

SendMessage ()メソッドは あまり高速ではなく1度のコールでダイレクトにコンポーネントを指定する呼び出しに比べて100サイクルぐらい遅いよ だからあんまり使わないほうがいいよ

的なことが書かれています。 な なんだってー (先に言え(# ゚Д゚)

100サイクルって何を基準にしてるんでしょうね?



ではSendMessage()は使えないのかというとそういうわけでもなくてオブジェクト数が少ない時や処理に余裕があるときは簡単便利なので問題ないと思います。 あとコンポーネントを直接呼び出せない場合 例えばStart()で他のオブジェクトを呼び出すときに処理がコケる場合があって そういう場合はSendMessage()なら通知が通ったりします 謎ですが。 早すぎるのもいけないのか?



General Performance Tips に他にも有効そうな情報が書いてありました


Find系関数は実行が遅いので 一度Find検索したらその結果をプールしておくとよい。」

Find関連メソッドも実行速度が遅いのですが シーン全体をサーチするのでオブジェクトが多いほど重くなります。

そこでFindメソッドはAwake()またはStart()文中で使用して配列に格納しておき実行時にはFindメソッドの代わりに配列を参照することで処理速度の向上が期待出来ます。

事前にオブジェクトを配列に登録しておく仕組みを 「オブジェクトプール」と呼んだりします。





■オブジェクトプール


実装方法)


Instantiate と Destroy のメソッドは両方とも重たい処理でアプリケーションの実行中に操作を行うと処理負荷が高いので

Instantiateで一度作成したコピーPrefabを配列に登録しておき SetActiveRecursively()を使用してモデルを非表示状態にします。

このときInstantiate の座標を原点にしないで下さい

※SetActiveRecursively()はモデルは非表示になりますが子供にしたパーティクルエフェクトなどはシーンに残ることがありますので カメラに映らない場所にオブジェクトを生成します



モデルを使用するときはオブジェクト名.SetActiveRecursively(true) でオブジェクトを表示状態にします



● spawnerのクラスの中でPrefabを作成する度にPrefabを配列に登録する


※Static型配列を使用すればどこからでもアクセスできます。

Prefabを削除するときは配列から登録を抹消します。



■ オブジェクトプールの例

オブジェクトプールのスクリプトはUnity本体にバンドルされている”Angrybots”のスクリプト

”Spawner.js”
を参照してみてください。

http://www.booncotter.com/unity-prefactory-a-better-pool-manager/

http://tasogare66.blog.fc2.com/blog-entry-16.html
C#にリライトされたバージョンがこちらに掲載されていました 



●Spawner.js



#pragma strict

static var spawner : Spawner;

var caches : ObjectCache[];

var activeCachedObjects : Hashtable;


class ObjectCache {
var prefab : GameObject;
var cacheSize : int = 10;

private var objects : GameObject[];
private var cacheIndex : int = 0;

function Initialize () {
objects = new GameObject[cacheSize];

// Instantiateしたオブジェクトを配列に格納して
for (var i = 0; i < cacheSize; i++) {
objects[i] = MonoBehaviour.Instantiate (prefab) as GameObject;
objects[i].SetActiveRecursively (false); // オブジェクトを非表示状態にする
objects[i].name = objects[i].name + i;
}
}

function GetNextObjectInCache () : GameObject {
var obj : GameObject = null;


obj = objects[cacheIndex];

// 非アクティブ状態 のオブジェクトみつかればそれを使用
if (!obj.active)
break;

// インデックスがキャッシュサイズを超えないように再計算
cacheIndex = (cacheIndex + 1) % cacheSize;

}

// アクティブ状態のオブジェクトが見つかれば削除する
if (obj.active) {
Debug.LogWarning (
"Spawn of " + prefab.name +
" exceeds cache size of " + cacheSize +
"! Reusing already active object.", obj);
Spawner.Destroy (obj);
}


// インデックスがキャッシュサイズを超えないように再計算
cacheIndex = (cacheIndex + 1) % cacheSize;
return obj;
}
}

function Awake () {

spawner = this;

//キャッシュするオブジェクト数の変数設定
var amount : int = 0;

// キャッシュ検索のループ
for (var i = 0; i < caches.length; i++) {
// Initialize each cache
caches[i].Initialize ();

// Count
amount += caches[i].cacheSize;
}

// オブジェクト個数分のハッシュテーブル配列の作成
activeCachedObjects = new Hashtable (amount);
}

static function Spawn (prefab : GameObject, position : Vector3, rotation : Quaternion) : GameObject {
var cache : ObjectCache = null;

// キャッシュ配列を検索して生成するオブジェクトが登録されているか検索
if (spawner) {
for (var i = 0; i < spawner.caches.length; i++) {
if (spawner.caches[i].prefab == prefab) {
cache = spawner.caches[i];
}
}
}

// キャッシュを検索して見つからなかったら普通にInstantiate() でオブジェクト生成
if (cache == null) {
return Instantiate (prefab, position, rotation) as GameObject;
}
// キャッシュ配列から次のオブジェクトを取りだす
var obj : GameObject = cache.GetNextObjectInCache ();

// オブジェクトにPosition,Rotation をセット
obj.transform.position = position;
obj.transform.rotation = rotation;
// オブジェクトを表示
obj.SetActiveRecursively (true);
spawner.activeCachedObjects[obj] = true;

return obj;
}

static function Destroy (objectToDestroy : GameObject) {
if (spawner && spawner.activeCachedObjects.ContainsKey (objectToDestroy)) {
objectToDestroy.SetActiveRecursively (false);
spawner.activeCachedObjects[objectToDestroy] = false;
}
else {
objectToDestroy.Destroy (objectToDestroy);
}
}



■ハッシュテーブル配列の拡張はtemp配列を作って一度配列を退避させた後もとの配列サイズを拡張して

CopyToメソッドを使用してデータをもとの配列にもどしてあげればよいです。

例)

static function EnlargeArrays(count:int, icount:int)
{
tempVerts:Vector3[] = Vertices;
Vertices = new Vector3[Vertices.Length + count];
   tempVerts.CopyTo(Vertices, 0);

}



■その他 空のGameObjectを作成してspawn時にPrefabをその子供として生成する方法

no title


配列を使わなくてもこんなかんじで親子にすれば 子供のtransform情報から配列が生成できます

可視化できるのでデバッグはしやすくなりますね








■オブジェクト配列の取得方法

例1<transforrmを直接配列に格納する場合>


for (var child : Transform in transform) {

実行文


}



例2<GetComponentsInChildrenを使用する場合>




var allChildren = gameObject.GetComponentsInChildren(Transform);

for (var child : Transform in allChildren) {

実行文

}



のようにFindメソッドを使わずにGameObjectの配列を取得することが出来ます

うまく使用すればシーン全体をサーチするFindメソッドに比べて高速な動作が期待出来て少し幸せ。





■まとめ:

●SendMessage()メソッドはあまり速度を期待できないので 出来る限りコンポーネントを直接呼び出すほうが

処理速度的な向上は期待出来ます。


●Findメソッドが全般に速度がいまいちなので一度Findしたデータは配列にとっておいて使いまわすことで

処理速度が改善できます。


●オブジェクトプール 配列にキャッシュすることでメモリ管理上は オブジェクト生成削除に起因したヒープ圧縮

による遅延が改善される効果があります。(以前の 高速化の2の記事を参照してください)


単純にNofitication Center ()をSendMessage()呼び出しを使用しない書き方にすれば良いような気もしたりなのですが。

Static変数を管理するスクリプトを作成してデータの一括管理でもいいような気もします そこらへんはケースバイケースで。。




今回は次世代機などで使用されているパーティクル描画の最適化手法である大量のパーティクルを描画するオフスクリーンパーティクルについてのまとめです。
ディファードレンダリングリアルタイムのテクスチャレンダリング のサポートが必要になりますので

プロバージョンの使用が前提です。そして現在Unityはモバイルのディファードレンダリングをサポートしていません。

ですのでPCでの使用に限られていまいますが、プロ版をお持ちでない方も こういう高速化技術があることを覚えておいていずれ活用していただければと思います。



デイファードレンダリングがどのようなものかを知っておく必要があります 。 日本語でわかりやすかったのが

■ 西川善司の3Dゲームファンのための「KILLZONE 2」グラフィックス講座(前編)

PS3のハイクオリティグラフィックスはDeferred Shadingでキマリ!?

http://game.watch.impress.co.jp/docs/series/3dcg/20090417_125909.html

ディファードレンダリング(遅延レンダリング)とは 同時に最大8枚のバッファに 法線データ、 カラーデータ、など様々なレンダリングに必要なデータをリアルタイムレンダリングして 合成の後フレームバッファに書き込むことで高品質なクオリティの映像を得るためのテクニックです。 各バッファに加工を施すことで多用なエフェクトなどを表現出来ます。 フレームバッファに書きこむだけのフォワードレンダリングに対して 合成後に遅れて表示バッファに書きこむため 遅延レンダリングと呼ばれます。




■GPU Gems 3:Chapter 23. High-Speed, Off-Screen Particles


http://http.developer.nvidia.com/GPUGems3/gpugems3_ch23.html

Nvidiaの公式サイトのデベロッパーセクションで無料公開されているGPUGems3内容に基づいて解説します。
パーティクルシステムは、多くの場合パフォーマンスの問題につながり、オーバードローを大量に消費するとレート
に影響します パーティクルシステムのエフェクトでは、たいていが多量のポリゴン、数百または数千が必要になります。
パーティクルエフェクトは多くの場合、あいまいでソフトな形状です。 例えば煙や霧のイメージは鋭いエッジなしで、低周波数のみを持っています。これらは視覚的な品質を損なうことなく 小さなサンプルで表すことができます(小さくレンダリングしても絵的にはあまり影響がない)。つまり低解像度のフレームバッファは十分有効ということが言えます。



■オフスクリーンレンダリング


パーティクルのサイズはメインフレームバッファのサイズの割合であるオフスクリーンレンダリングターゲットにレンダリングされます。ですが必ずしも同じサイズのバッファを必要とせず DX10から搭載されたダウンサンプリングの機能によって必要な品質のトレードオフに応じアプリケーションによってバッファの比率を選択することができます. 例えば、1/1、1/2、1/4、1/8 倍のスケールを選択できます(スケールは2の累乗に限定されません)

■オフスクリーン深度テスト

正確にシーン内の他の項目に対してパーティクルを閉塞するには深さのテストとそのサイズはオフスクリーンレンダリングターゲットと一致する深度バッファを必要とします

オフスクリーンレンダリングターゲットと一致する深度テストのための深度バッファ はメインのZバッファをダウンサンプルすることで取得します


  1. Deapth書き込みで通常通りにシーン内のすべてのソリッドオブジェクトをレンダリングします。

  2. 1の結果の深度バッファをダウンサンプリング。

  3. 小さなデプスバッファに対してテスト、オフスクリーンレンダリングターゲットにパーティクルをレンダリングします。

  4. パーティクルの描き込まれたテクスチャをアップサンプリング、メインフレームバッファに戻ってターゲットをレンダリングします。



【図1】
23fig02.jpg

【図2】





float4 particlePS(VS_OUT vIn): COLOR

float myDepth = vIn.depth;

float sceneDepth = tex2D(depthSampler, vIn.screenUV).x;

float zFade = saturate(scale * (myDepth - sceneDepth));

/ /Compute (r,g,b,a), etc.

return float4(r,g,b, a * zFade);




【図1】ゲームのスクリーン解像度に対して パーティクルのレンダリング解像度を1/4でダウンサンプリングした場合

(a) ゲーム画面解像度に対しZバッファの解像度が1:1の場合 パーティクルとオブジェクトのZ値は(0、1)で比較するが解像度が同等なので輪郭に影響は出ない

(b) ダウンサンプルしたZバッファでオブジェクトとパーティクルのZを比較する場合(0 or 1 =表示するしない)で判定すると輪郭部分に図のようなジャギーが出てしまう

(c) 画面解像度 4×4ピクセル=16ピクセル中にオブジェクトが専有するピクセル数 / 16 として平均値を求め平均値 からフェード値を得る コード中では zFade = saturate(scale * (myDepth - sceneDepth)); の部分

レンダリング時にカラー成分のアルファ値に a * zFade として境界部分のピクセルをぼかすことでエッジが目立たなくなります。





■ “High-Speed, Off-Screen Particles” in Unity

http://technology.blurst.com/off-screen-particle-optimization/


オフスクリーンパーティクルのUnityでの実例を検索してみたところ 最も早期に実現しているブログが有りましたが
このブログのコードはコード全体の一部分でしかなく再現することができませんでした

aaa





var blendMaterial:Material = PostProcessingHelper.GetMaterial(Shader.Find(“Hidden/Off-Screen Particles Composite”));

var texelOffset:Vector2 = Vector2.Scale(source.GetTexelOffset(), Vector2(source.width, source.height));

Graphics.BlitMultiTap(particlesRT, source, blendMaterial, texelOffset);



上記コードが問題の部分 ブログのコメント欄でも書かれていますが

PostProcessingHelper.GetPPCamera();

PostProcessingHelper.GetMaterial();


という見慣れないメソッドがありますが これがおそらく本人用にカスタマイズしたライブラリのメソッドではないかなということですね。その部分は以下のサイトでも触れられています。

Unity Community: http://forum.unity3d.com/threads/41177-High-speed-Off-screen-Particles-in-Unity

Unity3D по-русски:http://www.unity3d.ru/distribution/viewtopic.php?f=18&t=5665



ほかにもチャレンジしたプロジェクトはないのかと検索をしたところUnityAnserのほうに寄せられていた質問の中にサンプルのプロジェクトをアップされてる方がおられました。



■UnityAnser:Question about the off screen particle rendering

Hi,

I am trying to show the smoke particles in low resolution texture and then blend back with other parts in the scene. I read the article http://technology.blurst.com/off-screen-particle-optimization/ and make a simple test project inhttps://docs.google.com/open?id=0B1nYEeuWnX0DVHRaaS1uMG1EXzA

But unfortunately, the alpha transparent of particle textures seem not working correctly, they are just showing black. Anyone can help me how to correct this?

Thank you.

Q: オフスクリーンパーティクルを試しているんだけど パーティクルに黒い縁がついてしまうので誰か直してくれない?(意訳


ということらしいです。 ※リンク先からUnityプロジェクトのアーカイブがダウンロードできます




■オフスクリーンパーティクルの手順



no title




1.パーティクル専用のカメラを生成して パーティクルシステムをレイヤー分けして専用カメラのみに描画されるようにします。

2. リプレースメントシェーダー(Off-Screen Particles Replace.shader)によってパーティクルをテクスチャにレンダリングしzバッファともにダウンサンプリングします。 ダウンサンプリングされたZバッファはデプスバッファとしてメインフレームバッファとの合成時に背景と深度比較するために活用します。

これらのコードは OnPostRender() 内に記述します。OnPostRender()はフレームバッファへのレンダリングが終わったあとに呼ばれるメソッドです。

・コンポジットシェーダーOff-Screen Particles Composite.shader)で背景とパーティクルのバッファを合成します。

TestMainCamera.cs


using UnityEngine;
using System.Collections;

public class TestMainCamera : MonoBehaviour
{
public int factor = 2;
public Material blendMaterial;
public Shader HiddenReplaceShader;
public Shader CompositeShader;
public GameObject shaderCamera;
public Camera cam;
public RenderTexture particlesRT;
public RenderTexture source;

void Awake()
{

camera.depthTextureMode = DepthTextureMode.Depth;

particlesRT = RenderTexture.GetTemporary(Screen.width / factor, Screen.height / factor, 0);

}

void OnPostRender()
{

if (!enabled || !gameObject.active || !HiddenReplaceShader)

return;

if (!shaderCamera)
{

shaderCamera = new GameObject("ShaderCamera",typeof(Camera));

shaderCamera.camera.enabled = false;

//shaderCamera.hideFlags = HideFlags.HideAndDontSave;

}

cam = shaderCamera.GetComponent();
cam.CopyFrom (camera);
cam.cullingMask = 1 << LayerMask.NameToLayer("Test");
cam.backgroundColor = Color.black;
cam.clearFlags = CameraClearFlags.SolidColor;
cam.depthTextureMode = DepthTextureMode.None;
cam.targetTexture = particlesRT;
cam.RenderWithShader (HiddenReplaceShader, "RenderType");

Shader.SetGlobalVector("_CameraDepthTexture_Size", new Vector4(camera.pixelWidth, camera.pixelHeight, 0f,0f));    

blendMaterial.shader = CompositeShader;

Vector2 texelOffset = Vector2.Scale(new Vector2(0f,0f), new Vector2(Screen.width, Screen.height));

Graphics.BlitMultiTap(particlesRT, null, blendMaterial, texelOffset);
}


}




Off-Screen Particles Replace.shader



Shader "Hidden/Off-Screen Particles Replace" {
Category {

Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }

AlphaTest Greater .01

Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) }

CGINCLUDE

// Upgrade NOTE: excluded shader from OpenGL ES 2.0 because it uses non-square matrices

#pragma exclude_renderers gles
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

struct appdata {
float4 vertex:POSITION;
float4 texcoord:TEXCOORD0;
float4 color:COLOR;
};

struct v2f {
float4 pos:POSITION;
float3 uvz:TEXCOORD0;
float3 depth:TEXCOORD1;
float4 color:COLOR;
};

uniform sampler2D _MainTex : register(s0);
uniform sampler2D _CameraDepthTexture;
uniform float4 _CameraDepthTexture_TexelSize, _CameraDepthTexture_Size;
uniform float4 _TintColor;

v2f vert(appdata v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.color = v.color;
o.uvz.xy = MultiplyUV(UNITY_MATRIX_TEXTURE0, v.texcoord);

COMPUTE_EYEDEPTH(o.uvz.z);

float3x4 mat = float3x4 (
0.5, 0, 0, 0.5,
0, 0.5, 0, 0.5,
0, 0, 0, 1
);

o.depth = mul(mat, o.pos);

#ifdef SHADER_API_OPENGL
o.depth.xy *= _CameraDepthTexture_Size.xy;
#endif

return o;

}

half4 frag(v2f i):COLOR {
half4 texCol = tex2D(_MainTex, i.uvz.xy);
half4 outCol = i.color * _TintColor * texCol * 2;
#ifdef SHADER_API_D3D9
// Directx is off by half a texel
// so we need to do the projection seperately
i.depth.xy = i.depth.xy/i.depth.z;
// then offset the uvs by 0.5*TexelSize
i.depth.xy += 0.5 * _CameraDepthTexture_TexelSize.xy;
// we also need to flip the y if in directx
i.depth.y = 1 - i.depth.y + _CameraDepthTexture_TexelSize.y;
float z = tex2D(_CameraDepthTexture, i.depth.xy).r;
#else
float z = tex2Dproj(_CameraDepthTexture, i.depth).r;
#endif
float sceneDepth = DECODE_EYEDEPTH(z);
float myDepth = i.uvz.z;
if(sceneDepth < myDepth)

discard;

return outCol;

}

ENDCG

SubShader {

Pass {
Blend SrcAlpha OneMinusSrcAlpha
ColorMask RGB
CGPROGRAM
ENDCG
}
Pass {
Blend Zero OneMinusSrcAlpha
ColorMask A
CGPROGRAM
ENDCG
}

}

}

}





一応修正を入れてみました バグといえるほどのものではないのですがシェーダーでアルファチャンネルブレンドを
指定していますが テクスチャ自体にアルファが入っていないのでコンポジットをした時に透明バッファに値が渡らないわけです。
テクスチャを変えなくてもブレンド指定で加算かソフト加算を指定していれば結果は正確になるのですが

コードに素直に従って修正します。

no title

左側テクスチャのアルファ抜きをして右のテクスチャに変更をしました 問題点はそこだけです

bbb


Off-Screen Particles Composite.shader


Shader "Hidden/Off-Screen Particles Composite"
{

Properties
{
_MainTex ("Base (RGB)", RECT) = "white" {}
}

SubShader
{
Pass
{
ZTest Always Cull Off ZWrite Off Fog { Mode Off }

Blend One SrcAlpha

SetTexture[_MainTex] {combine texture}

}

}

Fallback Off



最終的な背景との合成は ”Off-Screen Particles Composite.shader” で行われます
(テクスチャーのコンバインだけの処理ですので説明は省きます。)
背景との合成方法はBlend に続く式で制御します パーティクルのアトリビュートではありません※赤文字部分

ccc

詳しは http://docs.unity3d.com/Documentation/Components/SL-Blend.html を参照してください


■パフォーマンスの結果について
ddd


実際にゲームに組み込んでみたところ 描画速度の向上にはかなり効果が高いようです
ゲームのスクリーン解像度が高くなるほど効果は顕著に現れます。


修正後のプロジェクトファイルをアップロードしました。 プロジェクト内でエフェクト用カメラにパーティクルのみを描画させるためのレイヤーを設定しているのですが パッケージではレイヤーの再設定が必要になるためプロジェクトファイルを圧縮してあります。

http://dl.dropbox.com/u/18978109/TestMixingResolution_re.zip


※ テスト用のスクリプトは多少アバウトに制作されていますので、使い勝手を上げるためにはシェーダーを自動検索させたり カメラを自動登録させたりといった処理は入れたほうがいいと思われます。





追記:
コードを見やすくするスクリプトをブログに組み込んではみたものの ものすごくHTMLの行数を稼いでしまうため
またいつものコードベタ張りになってしまいました。 コードはプロジェクトファイルのものをそのまま掲載していますので、そちらを参考にしてください。
あと不明な点や訪ねたいことがありましたら コメントしていただけると 調べて回答しますのでお気軽にどうぞ。
※ 最近グーグル検索に暗号化が施されるようになって検索ワードが読めないので 記事を作成する方針がたてにくくなってしまっています。

あと 記事が検索にかかりやすくと思ってつけた ”うにばな” ですが  ”うに ランク” などで検索があったりして思わぬ落とし穴でした 水産加工関係の方申し訳ありません (´・ω・`) 

ちなみに管理人はウニ大好きです大好きです


Network View


no title

Compnent ⇛Miscellous⇛NetworkViewでゲームオブジェクト(プレーヤー)にアタッチします

 

プロパティ

State Synchronization      ネットワークビューで使用される 同期の種類

Off:
状態の同期は使用されません。

Reliable Delta Compressed:

状態が変わっていない場合には何も追加されずに送信されます。パケット損失の場合、失われたパケットは自動的に再送信されます

Unreliable:
完了状態が送信されます。より多くの帯域幅を使用しますが、パケットロスの影響が最小限に抑えられます

 

Observed:
ネットワークを経由して送信されるデータ

View ID: このネットワークビューの識別子。

Scene ID:このシーンでネットワークビューのID 番号

Type:実行時 ​​に シーンまたは割り当てのいずれかに保存される

 

Reliable Delta Compressed またはUnreliableのデータ観測コンポーネントが自動的にネットワーク経由で送信されます

パケットは 常に送信された順序で受信されます。パケットが廃棄されている場合は、そのパケットが再送信されます。以前のパケットが受信されるまでそれ以降のすべてのパケットがキューに入れられます。最後のトランスミッションの値と現在の値が異なる場合は送信され 差がない場合は何も送信されません。

 

変換、アニメーション、剛体、またはスクリプト。どのような種類のコンポーネントでも観測できます

(インスペクタから値をobservedへドラッグドロップ)

 

NetworkView.isMine

・・・変数を使用してオブジェクトによって制御されるネットワークビューが送信する自分自身かの判別ができます

 

function OnNetworkInstantiate (info : NetworkMessageInfo) {
    if (networkView.isMine)
        Debug.Log("New object instanted by me");
    else
        Debug.Log("New object instantiated by " + info.sender);
}

 

手動でNetworkIDを設定する方法

●Network.AllocateViewID()

時々Network.Instansiateでセットアップがうまく動作しないことがあり そうした場合自分でNetworkViewIDを

割りつけてやる必要があります。

http://docs.unity3d.com/Documentation/ScriptReference/Network.AllocateViewID.html

 

EX)
var cubePrefab : Transform;
function OnGUI () {
if (
GUILayout.Button("SpawnBox")) {
var viewID =
Network.AllocateViewID();
networkView.
RPC("SpawnBox",
RPCMode.AllBuffered,
viewID,
transform.position);
}
}


@RPC
function SpawnBox (viewID : NetworkViewID, location : Vector3) {
// Instantate the prefab locally
var clone :
Transform;
clone = Instantiate(cubePrefab, location,
Quaternion.identity) as Transform;
var nView :
NetworkView;
nView = clone.GetComponent(
NetworkView);
nView.viewID = viewID;
}
 

送信周期の変更

※ “Edit → Project settings → Network”

 

Sendrate: 1秒間に何回のネットワークメッセージが送られるかの設定。 ※RPCメッセージに影響しません。

(ゲームが処理落ちしない最低限のトラフィックに調整してください)

 

スクリプトの変数の同期

OnSerializeNetworkView (stream : BitStream, info : NetworkMessageInfo)

BitStream ・・・ ステータス及び送信データのセット

isReading ・・・BitStreamがデータ読み込み状態か?

isWriting ・・・BitStreamがデータ書き込み状態か?

 

Serialize ・・・bool, char, short, int, float, Quaternion, Vector3 , NetworkPlayer.型などの値が返る

 

NetworkMessageInfo ・・・ 送信相手は誰か 送信された時間などの情報を受け取れます

timestamp ・・・送信されたメッセージの秒単位でのタイムスタンプ

sender ・・・誰からメッセージが送信されたのか 返り値はNetworkPlayer

 

EX)
var currentHealth : int;


function OnSerializeNetworkView(stream : BitStream, info : NetworkMessageInfo) {
var health : int = 0;
if (stream.isWriting) {
health = currentHealth;
stream.Serialize(health);
} else {
stream.Serialize(health);
currentHealth = health;
}
}
トラフィックの限度:スコープとグループの制限

送信するデータの量の制限によりネットワーク性能を大幅に改善することができます。
マルチプレイの環境ではプレーヤーは、情報のすべてのビットを受け取る必要はありません。
networkviewコンポーネントにはSetScope機能とgroup 設定があります

グループまたはプレーヤーに基づいて情報を送信しない機能です。

 

NetworkView.SetScope (player : NetworkPlayer, relevancy : bool) : bool


開始時 すべてのプレーヤーのためのデフォルトはtrueに設定されます。

たとえばプレーヤーからNetworkViewを設定したオブジェクトが十分遠くにあり情報を受け取る必要がない場合

値をfalseに設定すればNetworkViewを通じたデータ送信は行われません。 ※RPCには作用しません。

 

NetworkView.group ※groupナンバーはint型

すべてのネットワーク・メッセージやRPCの呼び出しで、設定されたグループ番号へ送信します。

※グループ番号の最大は32

function Awake() {
// Send everything from this network view through group 1
networkView.group = 1;
}

グループごとの送信を制限するための関数

SetReceivingEnabled (player : NetworkPlayer, group : int, enabled : bool) : void

・・・データ受信の可否

●SetSendingEnabled (group : int, enabled : bool) : void

・・・データ送信の可否


この2つの関数で 送信・受信の制限をすることができます。 たとえばゲームが32のレベルマップにわかれている場合 プレーヤーの周囲8方向のタイルにだけデータを送信することができます。 FPSなどでは必要ないかもしれませんがUnityのライブラリでMMOなどの大規模なゲームを作成する場合はこの機能が必要になってきます。

Unityエディタのオプション

■ “Edit → Project settings → Network”

Sendrate: 1秒間に何回のネットワークメッセージが送られるかの設定。RPCにはメッセージに影響しない。

                 ゲームが処理落ちしない最低限のトラフィックに調整してください

Debug level: ネットワークデバッグ情報のログ表示切替

■“Edit → Project settings → Player”

Run in background : サーバーをバックグラウンドで実行する

ネットワーク接続のセキュリティ

AES 暗号化, CRCs, ランダム暗号化、SYNCookies 、 RSA暗号化

  ひとつのコードで全てのセキュリティが有効になります

function StartServer ()
{
Network.InitializeSecurity();
Network.InitializeServer(32, 25000);
}

InitializeSecurity()をサーバに一度だけイニシャライズするだけです。15バイト分のパケット送信が追加されます。 

チート対策

ゲームを設計する場合、常に最悪のシナリオを仮定するべきです。
プレーヤーをコードに関して自分と同じだけ知っていると仮定します そして彼らは最悪にネットワーク・パケットの値を編集することができるとします。 したがって、受け取る値をすべて常にチェックします。
チートと格闘するためには特別のコードを必要としません  。 賢いゲーム/ネットワーク設計をするだけです。

プロキシの使用

リンク先を参照のこと

http://unity3d.com/support/documentation/ScriptReference/Network-useProxy.html

ラグの解消 : 予測、extrapolation と interpolation

エネミーにプレーヤー予測と同じ原理を適用することは可能です。
extrapolationは、最後に(バッファーされた)既知の位置、速度および方向でプレーヤーが次のフレームでどこに位置するかを予測します。
interpolationはパケットがクライアントへ行く途中にいつ落とされるかということです。相手は通常休止して新しいパケットが到着する時、最新の位置へ移動します。
あるセット量の時間(100ミリセカンドのような)までにゲームの状態を遅らせることによって、新しい最後の位置を挿入することでこれらの2点間の移動はパケットが落ちても滑らかに見えます。


さらにより正確な同期をさせるためにネットワークsendrateオプションを上げることで対応できます。

次回の記事で補足します

 

Network Loading

ネットワーク通信の間すべてのサーバー/クライアントPCの上で滑らかに動きます。
新しいクライアントがゲーム・ロビー場面で接続している一方で、ゲームを実行するサーバーを持つことができることを意味します。
これはサーバーがクライアントにAllBufferdでゲームオブジェクトの生成を送信をする時を以外問題はありません。
ゲームをロードする間には一時的なクライアント上のネットワーク通信をシャット・ダウンできます。
これはクライアント上で、サーバー接続が成功した直後に Network.isMessageQueueRunning = false; を呼ぶことにより行うことができます。
サーバーは、ネットワーク・グループを賢く使えば多数のゲーム・セッション/複数レベル(同じ場面でも)をhostすることができます。

※ 異なるゲーム・セッションでのプレーヤー間の衝突だけ注意してください。

ネットワーク開発のための外部リソース

ネットワークの知識を深めたい人は見てください

  • http://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking
  • http://developer.valvesoftware.com/wiki/Lag_Compensation
  • http://developer.valvesoftware.com/wiki/Working_With_Prediction
  • http://www.gamasutra.com/resource_guide/20020916/lambright_01.htm

    ●   http://202.16.132.23/unity3docsj/Documentation/Documentation/Components/net-HighLevelOverview.html

    『Unityで使える非同期処理のクラスライブラリ(iterator-tasks)を公開しました』

      Aimingの開発者さんのブログ http://developer.aiming-inc.com/

    ここに載せるのはちょっと違うような気もするのですが イテレータを使用して処理を複数のコルーチンに分散化して通信処理の遅延を解消する(たぶんそういうことかと)ライブラリだそうです。

    そこまで遅延したことがないのですが巨大なMMOタイプだと必要になるのかな

    Unityのネットワークサーバ

    マスターサーバー

    マスターサーバーは、ゲームマッチする手助けをします。サーバーを起動する際マスターサーバーに接続し、全てのアクティブなサーバーのリストを提供します。

    http://www.unity3d.com/master-server/index.html

    ●要するにUnityのマスターサーバーはマルチプレイゲームのマッチングを行うサーバーで ゲーム自体はそれぞれのプレーヤーをP2P接続するものということみたいです。最初にゲームを立ち上げたプレーヤのPCがHOSTになり その後ゲームにインしたプレーヤがクライアントになるというわけですね。

    マスターサーバーのカスタム記事はここらへん⇓を参考にしてください (力つきました・・

    http://d.hatena.ne.jp/nyakagawan/20110729/1311933651

    http://d.hatena.ne.jp/shinriyo/20110929

    その他のネットワークミドルウェア

    Unityのネットワーク接続に利用できるミドルウェアのリンクです

  • SmartFoxServer: http://www.smartfoxserver.com/             free
  • Netdog: http://www.netdognetworks.com/                             リンク切れ
  • RakNet: http://www.jenkinssoftware.com/                          収入が800万円(10万ドル)超えたらライセンス料?
  • Photon: http://photon.exitgames.com/                                  free (100CCUまで)
  • ElectroServer: http://www.electrotank.com/es5.html           free      (50CCUまで)
  • Badumna: www.badumna.com                                                16000円(200ドル:最大500CCU)ゲーム一本につき
  • こんにちは

    記事を書いてる途中ですが 所用ができたので中断してます で 更新に間があいてしまったので

    とりあえずアップしておきます

    (ブログのエディタにLiveWriterを使用していますが 保存ファイルを読み直すと書式がぶっ壊れてる仕様に泣かされています ある意味MS製品の通常運転)

     

     

    ●M2H

    http://www.m2h.nl/unity/

    現在ダウンロード出来ませんが わたしがUnityを初めたころ2009年末?にはチュートリアルなどのPDFファイルがフリーで公開されていました 現在はアセットストアのほうで$85でご提供なようです。 世の中おカネです

    http://www.scribd.com/doc/38400039/M2H-Unity-Networking-Tutorial

    当時のフリーのPDFと同じ物がここにアップされているようです。今回はこの情報をふまえて加筆修正してみます

     

    http://www.slideshare.net/ShinsukeSugita

    ネットワーク周りに関して こちらのスライドが上手くまとまっているようなので まとめかたを参考にさせていただきました




    その他 くわしくは後日webで



    ネットワークの状態

     

    ●Network.peerType

    ・・・接続状態(ピアタイプ)のステータス、サーバーまたはクライアントで接続しているかの判定をします

    NetworkPeerType. Connecting ・・・ ネットワーク接続中

    NetworkPeerType. Disconnected ・・・ ネットワーク切断中

    NetworkPeerType. Server    ・・・ サーバーとして接続

    NetworkPeerType. Client ・・・ クライアントとして接続

     

    EX)

    function OnGUI() {
    if (Network.peerType == NetworkPeerType.Disconnected)
    GUILayout.Label("Not Connected");
    else if (Network.peerType == NetworkPeerType.Connecting)
    GUILayout.Label("Connecting");
    else
    GUILayout.Label("Network started");
    }
    サーバの初期化

    Network.InitializeServer (connections : int, listenPort : int, useNat : boolean) : NetworkConnectionError

    (接続数、ポート番号, NATパンチスルー機能のオンオフ)

    function LaunchServer () {
    Network.incomingPassword = "HolyMoly";
    var useNat = !Network.HavePublicAddress();
    Network.InitializeServer(32, 25000, useNat);
    }

    接続数は接続が許可された着信接続(一般的にプレーヤーの数と同じではないことに注意)の数です。

    ListenPortは使用したいポート番号です。

    useNatは NATのパンチスルー機能を設定します。NATのパンチスルー機能を使用して接続を受け入れることができるようにしたい場合は、この属性をtrueに設定します。

    NAT接続 (Network.useNat)

    NATのパンチスルーについての解説(英文)

    http://www.raknet.net/raknet/manual/natpunchthrough.html


    NATについての詳細は、次のリンク先を見てください

    http://unity3d.com/support/documentation/Components/net-MasterServer.html

     

    『NAT(Network Address Translation)/NAPT(Network Address and Port Translation)とは』

    http://www.nic.ad.jp/ja/basics/terms/nat_napt.html

    NAT(Network Address Translation)とは、ある範囲のIPアドレスを別の範囲のIPアドレスと対応付け、 送受信する IPパケットのヘッダ部のIPアドレスを、対応付けられた範囲で変換する技術です。 NATは、プライベートアドレスとグローバルアドレスを相互に変換する用途によく使用されます。

    具体的には、IPパケットのヘッダ部の送信元アドレスがプライベートアドレスである場合に、 その送信元アドレスをグローバルアドレスに書き換えます。 また、該当グローバルアドレスを宛先アドレスとする応答パケットについて、 宛先アドレスをプライベートアドレスに変換します。 (以下本文にて

     

    サーバへの接続

     

    サーバ側    

    ●OnServerInitialized()

    ・・・ サーバ初期化され準備が出来た時に実行されます

    ●OnPlayerConnected (player : NetworkPlayer)

    ・・・ クライアントのプレーヤが接続した時に実行されます

    private var playerCount: int = 0;
    function OnPlayerConnected(player: NetworkPlayer) {
    Debug.Log("Player " + playerCount++ +
    " connected from " + player.ipAddress +
    ":" + player.port);
    // Populate a data structure with player information ...
    }

    OnPlayerDisconnected (player:NetworkPlayer)

    ・・・ クライアントのプレーヤが切断した時に実行されます

    function OnPlayerDisconnected(player: NetworkPlayer) {
    Debug.Log("Clean up after player " + player);
    Network.RemoveRPCs(player);
    Network.DestroyPlayerObjects(player);
    }

    クライアント側      

     

    ●Network.Connect (IP : String, remotePort : int, password : String = "")

    ・・・指定されたホスト(IPまたはドメイン名)とサーバー・ポートに接続します。

    function ConnectToServer () {
    Network.Connect("127.0.0.1", 25000);
    }

    ●Network.Disconnect (timeout : int )

    ・・・サーバーからの切断 timeout:切断までの待ち時間

    function OnGUI() {
    if (GUILayout.Button ("Disconnect")) {
    Network.Disconnect();
    MasterServer.UnregisterHost();
    }
    }

    ●OnConnectedToServer()

    ・・・サーバー接続に成功した時に実行されます

    function OnConnectedToServer() {
    Debug.Log("Connected to server");
    // Send local player name to server ...
    }

    ●OnDisconnectedFromServer (mode : NetworkDisconnection)

    ・・・サーバーから切断された時に実行されます

    function OnDisconnectedFromServer(info : NetworkDisconnection) {
    if (Network.isServer) {
    Debug.Log("Local server connection disconnected");
    }
    else {
    if (info == NetworkDisconnection.LostConnection)
    Debug.Log("Lost connection to the server");
    else
    Debug.Log("Successfully diconnected from the server");
    }
    }
    ●OnFailedToConnect (error : NetworkConnectionError)

    ・・・サーバーへの接続失敗

    function OnFailedToConnect(error: NetworkConnectionError) {
    Debug.Log("Could not connect to server: "+ error);
    }
    オブジェクトの生成 と 破壊

    Network. Instansiate()

    ・・・ネットワーク越しのオブジェクトの生成

    Instantiate (prefab : Object, position : Vector3, rotation : Quaternion, group : int) : Object

    EX)
    // Immediately instantiate new connected player's character
    // when successfully connected to the server.

    var playerPrefab : Transform;
    function OnConnectedToServer () {
    Network.Instantiate(playerPrefab, transform.position, transform.rotation, 0);
    }
     

    ●Network. Destroy()

    ・・・ネットワーク越しのオブジェクトの破壊
    EX)
    var timer : float = 0;
    function Awake () {
    timer = Time.time;
    }
    // Network destroy the object which has this script
    // it must have a NetworkView attached
    function Update() {
    if (Time.time - timer > 2) {
    Network.Destroy(GetComponent(NetworkView).viewID);
    }
    }
     
    var timer : float = 0;
    function Awake () {
    timer = Time.time;
    }
    // Network destroy the object which has this script
    function Update() {
    if (Time.time - timer > 2) {
    Network.Destroy(gameObject);
    }
    }
     

    RPC buffer によるネットワーク越しのオブジェクトの破壊:


    function OnNetworkInstantiate(info: NetworkMessageInfo) {
    Debug.Log(networkView.viewID + " spawned");
    if (Network.isServer) {
    Network.RemoveRPCs(networkView.viewID);
    Network.Destroy(gameObject);
    }
    }
    RPC(RemotePrecedureCall) ・・ 遠隔スクリプトの呼び出し
     Unityのネットワークを介してスクリプトをリモートで呼び出すことができます。

    ※この関数は 送信側と供給側の両方に存在する必要があります。

    RPCコールで送ることができる変数の種類

  • int
  • float
  • string
  • NetworkPlayer
  • NetworkViewID
  • Vector3
  • Quaternion

    RPCファンクションを有効にするには javascriptなら”@RPC" C#なら”[RPC]”をファンクションの前につけます

    EX)
    networkView.RPC ("PrintText", RPCMode.All, "Hello world");
    @RPC function PrintText (text : String, info : NetworkMessageInfo) { Debug.Log(text + " from " + info.sender); }
     
    NetworkView.RPC (name : String, mode : RPCMode, params args : object[])

    (”ファンクションネーム”, RPCモード, viewID, transform.positionなどの値)

     
    EX)
    var cubePrefab : Transform;
    function OnGUI () {
    if (GUILayout.Button("SpawnBox")) {
    var viewID : NetworkViewID= Network.AllocateViewID();
    networkView.RPC("SpawnBox", RPCMode.AllBuffered, viewID, transform.position);
    }
    }


    @RPC
    function SpawnBox (viewID : NetworkViewID, location : Vector3) {
    // Instantate the prefab locally
    var clone : Transform;
    clone = Instantiate(cubePrefab, location, Quaternion.identity) as Transform;
    var nView : NetworkView;
    nView = clone.GetComponent(NetworkView);
    nView.viewID = viewID;
    }
     

    RPCModeの種類

    RPCMode.Serverサーバーだけに送信
    RPCMode.Others送信者を除く全体に送信
    RPCMode.OthersBufferedBufferedモードで送信者を除く全体に送信
    RPCMode.All送信者を含む全体に送信
    RPCMode.AllBuffered Bufferedモードで送信者を含む全体に送信
     ●Buffered とは 

    バッファーを用いた送信方法です。新規プレーヤーが接続する場合に 新規プレーヤーがこのメッセージを受け取ります。

    ●たくさんのプレーヤーを生成するのに有用です。

  • Unite2012の講義内容をまとめました

    元ネタは

     

    『Performance Optimization Tips And Tricks For Unity』

                    Jonas Echterhoff & Kim Steen Riber

    unite2012 perfomance

    http://video.unity3d.com/video/6957357/unite-2012-performance

     

    なのですが GPUの最適化に関しては、ほぼ ?にまとまっていました(ただし英語

    http://docs.unity3d.com/Documentation/Manual/OptimizingGraphicsPerformance.html

    シェーダ解説で一度解説したような気もするので 8月頃の記事を参照してください

    忙しくて見れていなかったための自分用まとめですので すでに見た方はスルーしてください

     

     

     

    『Unityのパフォーマンスの最適化 TIPSと方策』

     

    プロファイラを見てピークに注目!

    ●スクリプトの最適化

    ●プログラムのアルゴリズムの最適化101 (ここではすべてを解説しません)

    ●見えているオブジェクトに対してスクリプトを削ることができるかもしれません

    OnBecameVisible,OnBecameInvisible を使用して callbackし無駄にスクリプトを使用しないようにします。

     

    ●毎フレーム必要のないUpdateを呼ぶよりも コルーチンを使用しましょう

    //毎フレーム呼び出し
     
    void Update() {
     
    }
     
    //0.2秒間隔での呼び出し
     
    IEnumrator Start() {
     
       while(true) {
     
            yield return new WaitForSeconds (0.2f);
     
       }
     
    }
     

    ● UnityエンジンのAPIメソッドが毎フレームの呼び出しをする代わりに 結果を保持するように考慮してください

    これは C#におけるゲッタープロパティに当てはまります

    たとえば Transform.positionはグローバルPosition の階層を繰り返し呼び出します

     

    ■メモリーの最適化

    ●メモリの種類

    Mono Memory

    ●Script Object

    ● Wrappers for

    ●Game Object

    ● Asetts

    ●Components

    Garbage collected

    Unity Internal Memory

    ●Asset data

    ●Textures,meshs,animation,etc。

    ●GameObjects

    ●Internals (Rendering,particles,physic,etc)

     

     

    ●Monoが割りつけたヒープブロック

    ●実行時に必要となったヒープサイズのブロックを追加する

    ●フラグメンテーション(メモリの分断)はメモリが消耗していないとしても新しく追加されたヒープブロックによって生じます

    ●ガベージコレクターがクリーンをおこうなうとき

    ヒープブロックを利用可能なメモリのサイズに割付けられなかった時

    System.GC.Collect()ガベージ・コレクタを明示的に動作させます が 処理不可が高いので実行は注意

     

    class MyClass{
     
      public int a,b,c;
     
    }
     
    struct MyStruct{
     
     public int a,b,c;
     
    }
     
    void Update () {
     
      //Bad  アロケートメモリがヒープメモリ上に発生し あとでガベージコレクトをすることになる
     
      MyClass.c    = new MyClass();
     
     // Good アロケートメモリはスタック上に発生し ガベージコレクションが発生しない
     
      MyStruuct  s = new MyStruct();
     
     

          注意) この意味合いは 作業メモリの拡張方法なのですが ヒープメモリの場合はMONOが自由にメモリを管理して

    割り付けることで内部的にはメモリ中の開いたところにバラバラに管理され 作業メモリへのインデックスを保持しています。使用後のメモリはガベージ(ごみ)となって残るために一定時間ごとに圧縮(ヒープソート圧縮)を行ってメモリを開放する必要があります。

    それに対しスタックメモリはレジスタを使用してメモリを格納順に積み上げる(スタック)して使用後は最後に積んだメモリから開放します。そのため大規模なソートを行わないので遅延が発生しません。 一番近いイメージはJavaの配列やジェネリック関数のリスト構造でしょうか ちょっと違う?

     

    ●あなたのやり方でメモリマネージを実装する場合 再利用不能なクラス を割り付ける前にリストを使用してみてください

    ●オブジェクトプールの実例はアセットストアで『Astro Dude』を見つけてください

    ●インスタンス処理は重たいです

    ●オブジェクトの事前読み込みを使用しましょう

    ●再び登場させるオブジェクト(bullet、Explosionなど)GameObject を最適化する時にInstantiateDestroyの代わりに

    inactiveのリストにして保持します。

     

    EX1はint型を何度も宣言していて メモリ効率が悪いで例です。

    EX1)

    Public class AnAllocatingWorker {  
    class WorkData {  
    public int value  = 1; 
    } 
    WorkData CreateWorkData(){    
    return new WorkData(); 
    } 
    void ProcessWorkData(WorkData data){ 
    int[] TempWorkBuffer = new int[128*1024]; TempWorkBuffer[0] = data.value; 
    } 
    public void Update() {  
    ProcessWorkData(CreateWorkData());; 
    } 
    }
     

    EX2)

    Public class AnAllocatingWorker {  
    class WorkData {  
    public int value  = 1; 
    } 
    WorkData CreateWorkData(){    
    return new WorkData(); 
    } 
    int[] TempWorkBuffer = new int[128*1024]; 
    void ProcessWorkData(WorkData data){ 
    TempWorkBuffer[0] = data.value; 
    } 
    public void Update() {  
    ProcessWorkData(CreateWorkData());; 
    } 

     

     

    ●UnityのビルトインGUIシステムをゲームに使用しないでください ガベージコレクションと相性がよくありません

    ●ストリーミングしないゲームではいつもゲーム中にアロケーションが発生するわけではありません

    ●GetRuntimeMemorySize(Object) を使用します。 GetMonoHeapSize と GetMonoUseSize を使用してメモリ使用量を追跡します。

    ●メッシュコライダーよりもプりミティブなコライダーのほうが最適化に良好です

    Time.fixedDeltaTime (project settings ->Time)  の設定をします。おそらくゆっくり動くゲームの時はfixedUpdatesよりも良い結果をもたらします

     

    ●最も一般的なパフォーマンス低下の落とし穴はphysicsです

    ●けして動くことのない静止したコライダー(例えば リジッドボディを伴わないコライダー)

    ●プロファイラーで『Static Collide.Move』で表示されるものですが プロセスは物理演算を行なっています

     

    ●GPUとCPUの限界までスピードをあげられるのか

    ●GPUのパフォーマンスはレンダリングされるピクセルの最大数とメモリの帯域幅で決定されます(フィルレート)

    ●CPUのパフォーマンスは ドローコール処理の上限にしばられることがあります

     

    ●GPUプロファイラはドローコールあたりの消費時間を見つけることに役立ちます

    ●ドローコールを解消することでGPUとCPU両方のレンダリング処理が軽減されます。最初にできる限りのドローコールを削減してみましょう

    ●その結果 オブジェクトを高速にレンダリングするにはどうすればいいいかを知ることができます

     

    ●少数のオブジェクトでレンダリングすることでパフォーマンスは改善します

    ●シーンを簡略化する

    ●積極的にカリング(オクルージョンかリング、円錐台カリング)を使用する

    ●LODを使用する

      注意)現在主流のGPUでは1ピクセル中にあまりに多くのポリゴンが集中すると処理落ちが発生するようです。そこで積極的にLOD

          (少ないポリゴンのモデルを段階ごとに持つ)をすることでカメラから遠距離にポリゴン数の多いモデルが表示されないようにす  る必要があるようです。

    ●パフォーマンス改善のためにシェーダーパスを削減します

    ●シーンセットアップ時にパスの少ないシェーダーを選択する(リフレクション。シャドー。ピクセルライトはフォワードレンダリングにおいてすべてのパスが加算されます)

    ●フォワードレンダーのピクセルライトは低速です。可能なかぎりリアルタイムライティングを避けてライトマッピングを使用しましょう

    ●ピクセルライトの数をクオリティセッティングで調節します。

    ●CPUパフォーマンスはドローコール処理の上限に影響されます

    ●注意:現在のハードウェアではフレームあたりのドローコールはモバイルで数百、デスクトップで数千を超えないようにしてください


    ●ドローコールを減らすために 近接するオブジェクトはひとつのメッシュにまとめ 一緒に描画されるようにします。

    ●手動ではソースアセットの管理

    ●自動ではUnityのドローコールバッチを使用します → ドローコールバッチのリファレンス

    「dynamic batching」について。unity3dfan @ ウィキ http://www38.atwiki.jp/unity3dfan/pages/42.html 

       注意)Unityは自動で900頂点(UV保持時は300頂点)までのポリゴンを自動でひとまとめに処理するダイナミックバッチと呼ばれる機能が搭載されていまあす。 マテリアルとテクスチャはひとつにまとめておくことが条件です。 ほかにスタティックバッチ インスペクタの右上のstaticのチェックをいれることで機能するものもあります。これは背景などの静止物に主に使用します。

    ●メッシュの中に含まれる異なるマテリアルはなるべく少なくしてください

    ●可能ならばテクスチャのアトラス化をしてください  注(アトラス化 複数のテクスチャを一枚のテクスチャにまとめること)

    ●多くのオブジェクトが複数のライトの影響を受けているとか 効率良くカリングを行わないとドローコールバッチはパフォーマンスを悪化させてしまいます 

     

    ●GPUのパフォーマンスはレンダリングされるピクセルの最大数とメモリの帯域幅で決定されます(フィルレート)

     ●フィルレートの限界を知ることは簡単です。高速にゲームを動かした時ディスプレイ解像度が減少しますか? もしそうなるならそれがフィルレートの限界です

     ●同様に、クオリティセッティングでテクスチャクオリティを下げてみます。ゲームをが早くなるときは そこがおそらくメモリの帯域限界です。

     ●フィルレートが限界ならば最適化としてシェーダーを簡略化してみましょう

     

    ●モバイルのGPUの時:アルファテストを除外します。

    ●簡潔で整理されたシェーダコードをつかいましょう(たとえばUnityに付属の『Mobile』シェーダーのような)

     

    ●シェーダーコードの最適化:

    ● 効率の悪い関数を避けます(pow,exp,log,cos,sin,tan,など)。テクスチャ行列の事前計算も考慮してみます

    ●モバイルの場合:可能な限り低精度の変数を使用する(Cgにおけるfloat,half,fixed)

     

    ●もしメモリ帯域の限界ならばテクスチャメモリを減少させます

    ●テクスチャ圧縮を16ビットに

    ●テクスチャサイズを縮小(シーンビューンのレンダーモードのミップマップのチェック)

    ●テクスチャにミップマップを設定する。

    ●複雑なジオメトリによってもGPUのパフォーマンス限界はもたらされます

    ●必要以上のポリゴントライアングルは使用しない

    ●UV継ぎ目とハードエッジ(頂点に複数の法線)は可能な限り減らす

    ●シンプルなシェーダーを使用しましょう

     

     

    ■最適化に関する下記の項目も参照してみてください

    Draw Call Batching

    Shader Performance page.

    Modeling Characters for Optimal Performance




    ※2014 8.16 うにばな 講座みたいなもの番外編(高速化(最適化)について3 モバイル系                      投稿しました よろしければ参考にどうぞ 

    ↑このページのトップヘ