万年素人からHackerへの道

万年素人がHackerになれるまで殴り書きするぜ。

  • ・資産運用おすすめ
    10万円は1000円くらい利益
    資産運用ブログ アセマネ
    • ・寄付お願いします
      YENTEN:YYzNPzdsZWqr5THWAdMrKDj7GT8ietDc2W
      BitZenny:ZfpUbVya8MWQkjjGJMjA7P9pPkqaLnwPWH
      c0ban:8KG95GXdEquNpPW8xJAJf7nn5kbimQ5wj1
      Skycoin:KMqcn7x8REwwzMHPi9fV9fbNwdofYAWKRo

    UnityのJSの型キャスティング

    networkView.RPC("SpawnCastle", RPCMode.AllBuffered, viewID, new Vector3( 40,1, 40), playerList[0].number);

    アプリで吐き出すのなら型をはっきりしなくても勝手にダック・タイピングになるけど、
    Androidで吐き出すとなると型が厳しくる
    上のコードはコンパイル無理。

    networkView.RPC("SpawnCastle", RPCMode.AllBuffered, viewID, new Vector3( 40,1, 40), (playerList[0] as CastlePlayer).number);

    メソッドを実行したものを変数へ代入なしでそのまま使いたいときはこうする
    "as"がいる。今回はCastlePlayer型でキャストした。

    Unity MasterServerの用意 Unity

    参考:http://d.hatena.ne.jp/nyakagawan/20110729/1311933651
    (注意)Pro版でないとできないっぽい

    DL先:http://unity3d.com/master-server/index.html
    解答時にフォルダが生成されないので自分で作る

    mkdir SrcRoot
    cd SrcRoot
    unzip MasterServer-2.0.f1.zip

    ncursesが必要なのでportでインストール

    sudo port install ncurses

    →会社とかではできないかも?

    cd SrcRoot
    make

    「MasterServer」という実行ファイルが出来る

    • UnityプロジェクトをDLする(MasterServerへ接続するサンプル)

    DL先:http://unity3d.com/support/resources/example-projects/networking-example.html

    • Car.js(最新版ではバグなので修正する)
    #pragma strict
    
    //maximal corner and braking acceleration capabilities
    var maxCornerAccel=10.0;
    var maxBrakeAccel=10.0;
    
    //center of gravity height - effects tilting in corners
    var cogY = 0.0;
    
    //engine powerband
    var minRPM = 700;
    var maxRPM = 6000;
    
    //maximum Engine Torque
    var maxTorque = 400;
    
    //automatic transmission shift points
    var shiftDownRPM = 2500;
    var shiftUpRPM = 5500;
    
    //gear ratios
    var gearRatios = [-2.66, 2.66, 1.78, 1.30, 1.00];
    var finalDriveRatio = 3.4;
    
    //a basic handling modifier:
    //1.0 understeer
    //0.0 oversteer
    var handlingTendency = 0.7;
    
    //graphical wheel objects
    var wheelFR : Transform;
    var wheelFL : Transform;
    var wheelBR : Transform;
    var wheelBL : Transform;
    
    //suspension setup
    var suspensionDistance = 0.3;
    var springs = 1000;
    var dampers = 200;
    var wheelRadius = 0.45;
    
    //particle effect for ground dust
    var groundDustEffect : Transform;
    
    private var queryUserInput = true;
    private var engineRPM : float;
    private var steerVelo = 0.0;
    private var brake = 0.0;
    private var handbrake = 0.0;
    private var steer = 0.0;
    private var motor = 0.0;
    private var skidTime = 0.0;
    private var onGround = false;
    private var cornerSlip = 0.0;
    private var driveSlip = 0.0;
    private var wheelRPM : float;
    private var gear = 1;
    private var skidmarks : Skidmarks;
    private var wheels : WheelData[];
    private var wheelY = 0.0;
    private var rev = 0.0;
    
    //Functions to be used by external scripts 
    //controlling the car if required
    //===================================================================
    
    //return a status string for the vehicle
    function GetStatus(gui : GUIText) {
    	gui.text="v="+(rigidbody.velocity.magnitude * 3.6).ToString("f1") + " km/h\ngear= "+gear+"\nrpm= "+engineRPM.ToString("f0");
    }
    
    //return an information string for the vehicle
    function GetControlString(gui : GUIText) {
    	gui.text="Use arrow keys to control the jeep,\nspace for handbrake.";
    }
    
    //Enable or disable user controls
    function SetEnableUserInput(enableInput :boolean) {
    	queryUserInput=enableInput;
    }
    
    //Car physics
    //===================================================================
    
    //some whee calculation data
    class WheelData{
    	var rotation = 0.0;
    	var coll : WheelCollider;
    	var graphic : Transform;
    	var maxSteerAngle = 0.0;
    	var lastSkidMark = -1;
    	var powered = false;
    	var handbraked = false;
    	var originalRotation : Quaternion;
    };
    
    function Start () {
    	//setup wheels
    	wheels=new WheelData[4];
    	for(var i:int=0;i<4;i++)
    		wheels[i] = new WheelData();
    		
    	wheels[0].graphic = wheelFL;
    	wheels[1].graphic = wheelFR;
    	wheels[2].graphic = wheelBL;
    	wheels[3].graphic = wheelBR;
    
    	wheels[0].maxSteerAngle=30.0;
    	wheels[1].maxSteerAngle=30.0;
    	wheels[2].powered=true;
    	wheels[3].powered=true;
    	wheels[2].handbraked=true;
    	wheels[3].handbraked=true;
    
    	for(w in wheels)
    	{
    		if(w.graphic==null)
    			Debug.Log("You need to assign all four wheels for the car script!");
    		if(!w.graphic.transform.IsChildOf(transform))	
    			Debug.Log("Wheels need to be children of the Object with the car script");
    			
    		w.originalRotation = w.graphic.localRotation;
    
    		//create collider
    		var colliderObject = new GameObject("WheelCollider");
    		colliderObject.transform.parent = transform;
    		colliderObject.transform.position = w.graphic.position;
    		w.coll = colliderObject.AddComponent(WheelCollider);
    		w.coll.suspensionDistance = suspensionDistance;
    		w.coll.suspensionSpring.spring = springs;
    		w.coll.suspensionSpring.damper = dampers;
    		//no grip, as we simulate handling ourselves
    		w.coll.forwardFriction.stiffness = 0;
    		w.coll.sidewaysFriction.stiffness = 0;
    		w.coll.radius = wheelRadius;
    	}	
    
    	//get wheel height (height forces are applied on)
    	wheelY=wheels[0].graphic.localPosition.y;
    	
    	//setup center of gravity
    	rigidbody.centerOfMass.y = cogY;
    	
    	//find skidmark object
    	skidmarks = FindObjectOfType(typeof(Skidmarks));
    	
    	//shift to first
    	gear=1;
    }
    
    //update wheel status
    function UpdateWheels()
    {
    	//calculate handbrake slip for traction gfx
     	var handbrakeSlip=handbrake*rigidbody.velocity.magnitude*0.1;
    	if(handbrakeSlip>1)
    		handbrakeSlip=1;
    		
    	var totalSlip=0.0;
    	onGround=false;
    	for(w in wheels)
    	{		
    		//rotate wheel
    		w.rotation += wheelRPM / 60.0 * -rev * 360.0 * Time.fixedDeltaTime;
    		w.rotation = Mathf.Repeat(w.rotation, 360.0);		
    		w.graphic.localRotation= Quaternion.Euler( w.rotation, w.maxSteerAngle*steer, 0.0 ) * w.originalRotation;
    
    		//check if wheel is on ground
    		if(w.coll.isGrounded)
    			onGround=true;
    			
    		var slip = cornerSlip+(w.powered?driveSlip:0.0)+(w.handbraked?handbrakeSlip:0.0);
    		totalSlip += slip;
    		
    		var hit : WheelHit;
    		var c : WheelCollider;
    		c = w.coll;
    		if(c.GetGroundHit(hit))
    		{
    			//if the wheel touches the ground, adjust graphical wheel position to reflect springs
    			w.graphic.localPosition.y-=Vector3.Dot(w.graphic.position-hit.point,transform.up)-w.coll.radius;
    			
    			//create dust on ground if appropiate
    			if(slip>0.5 && hit.collider.tag=="Dusty")
    			{
    				groundDustEffect.position=hit.point;
    				groundDustEffect.particleEmitter.worldVelocity=rigidbody.velocity*0.5;
    				groundDustEffect.particleEmitter.minEmission=(slip-0.5)*3;
    				groundDustEffect.particleEmitter.maxEmission=(slip-0.5)*3;
    				groundDustEffect.particleEmitter.Emit();								
    			}
    			
    			//and skid marks				
    			if(slip>0.75 && skidmarks != null)
    				w.lastSkidMark=skidmarks.AddSkidMark(hit.point,hit.normal,(slip-0.75)*2,w.lastSkidMark);
    			else
    				w.lastSkidMark=-1;
    		}
    		else w.lastSkidMark=-1;
    	}
    	totalSlip/=wheels.length;
    }
    
    //Automatically shift gears
    function AutomaticTransmission()
    {
    	if(gear>0)
    	{
    		if(engineRPM>shiftUpRPM&&gear<gearRatios.length-1)
    			gear++;
    		if(engineRPM<shiftDownRPM&&gear>1)
    			gear--;
    	}
    }
    
    //Calculate engine acceleration force for current RPM and trottle
    function CalcEngine() : float
    {
    	//no engine when braking
    	if(brake+handbrake>0.1)
    		motor=0.0;
    	
    	//if car is airborne, just rev engine
    	if(!onGround)
    	{
    		engineRPM += (motor-0.3)*25000.0*Time.deltaTime;
    		engineRPM = Mathf.Clamp(engineRPM,minRPM,maxRPM);
    		return 0.0;
    	}
    	else
    	{
    		AutomaticTransmission();
    		engineRPM=wheelRPM*gearRatios[gear]*finalDriveRatio;
    		if(engineRPM<minRPM)
    			engineRPM=minRPM;
    		if(engineRPM<maxRPM)
    		{
    			//fake a basic torque curve
    			var x = (2*(engineRPM/maxRPM)-1);
    			var torqueCurve = 0.5*(-x*x+2);
    			var torqueToForceRatio = gearRatios[gear]*finalDriveRatio/wheelRadius;
    			return motor*maxTorque*torqueCurve*torqueToForceRatio;
    		}
    		else
    			//rpm delimiter
    			return 0.0;
    	}
    }
    
    //Car physics
    //The physics of this car are really a trial-and-error based extension of 
    //basic "Asteriods" physics -- so you will get a pretty arcade-like feel.
    //This may or may not be what you want, for a more physical approach research
    //the wheel colliders
    function HandlePhysics () {
    	var velo=rigidbody.velocity;
    	wheelRPM=velo.magnitude*60.0*0.5;
    
    	rigidbody.angularVelocity=new Vector3(rigidbody.angularVelocity.x,0.0,rigidbody.angularVelocity.z);
    	var dir=transform.TransformDirection(Vector3.forward);
    	var flatDir=Vector3.Normalize(new Vector3(dir.x,0,dir.z));
    	var flatVelo=new Vector3(velo.x,0,velo.z);
    	var rev=Mathf.Sign(Vector3.Dot(flatVelo,flatDir));
    	//when moving backwards or standing and brake is pressed, switch to reverse
    	if((rev<0||flatVelo.sqrMagnitude<0.5)&&brake>0.1)
    		gear=0;
    	if(gear==0)
    	{	
    		//when in reverse, flip brake and gas
    		var tmp=brake;
    		brake=motor;
    		motor=tmp;
    		
    		//when moving forward or standing and gas is pressed, switch to drive
    		if((rev>0||flatVelo.sqrMagnitude<0.5)&&brake>0.1)
    			gear=1;
    	}
    	var engineForce=flatDir*CalcEngine();
    	var totalbrake=brake+handbrake*0.5;
    	if(totalbrake>1.0)totalbrake=1.0;
    	var brakeForce=-flatVelo.normalized*totalbrake*rigidbody.mass*maxBrakeAccel;
    
    	flatDir*=flatVelo.magnitude;
    	flatDir=Quaternion.AngleAxis(steer*30.0,Vector3.up)*flatDir;
    	flatDir*=rev;
    	var diff=(flatVelo-flatDir).magnitude;
    	var cornerAccel=maxCornerAccel;
    	if(cornerAccel>diff)cornerAccel=diff;
    	var cornerForce=-(flatVelo-flatDir).normalized*cornerAccel*rigidbody.mass;
    	cornerSlip=Mathf.Pow(cornerAccel/maxCornerAccel,3);
    	
    	rigidbody.AddForceAtPosition(brakeForce+engineForce+cornerForce,transform.position+transform.up*wheelY);
    	
    	var handbrakeFactor=1+handbrake*4;
    	if(rev<0)
    		handbrakeFactor=1;
    	var veloSteer=((15/(2*velo.magnitude+1))+1)*handbrakeFactor;
    	var steerGrip=(1-handlingTendency*cornerSlip);
    	if(rev*steer*steerVelo<0)
    		steerGrip=1;
    	var maxRotSteer=2*Time.fixedDeltaTime*handbrakeFactor*steerGrip;
    	var fVelo=velo.magnitude;
    	var veloFactor=fVelo<1.0?fVelo:Mathf.Pow(velo.magnitude,0.3);
    	var steerVeloInput=rev*steer*veloFactor*0.5*Time.fixedDeltaTime*handbrakeFactor;
    	if(velo.magnitude<0.1)
    		steerVeloInput=0;
    	if(steerVeloInput>steerVelo)
    	{
    		steerVelo+=0.02*Time.fixedDeltaTime*veloSteer;
    		if(steerVeloInput<steerVelo)
    			steerVelo=steerVeloInput;
    	}
    	else
    	{
    		steerVelo-=0.02*Time.fixedDeltaTime*veloSteer;
    		if(steerVeloInput>steerVelo)
    			steerVelo=steerVeloInput;
    	}
    	steerVelo=Mathf.Clamp(steerVelo,-maxRotSteer,maxRotSteer);	
    	transform.Rotate(Vector3.up*steerVelo*57.295788);
    }
    
    function FixedUpdate () {
    	//query input axes if necessarry
    	if(queryUserInput)
    	{
    		brake = Mathf.Clamp01(-Input.GetAxis("Vertical"));
    		handbrake = Input.GetButton("Jump")?1.0:0.0;
    		steer = Input.GetAxis("Horizontal");
    	 	motor = Mathf.Clamp01(Input.GetAxis("Vertical"));
     	}
    	else
    	{
    		motor = 0;
    		steer = 0;
    		brake = 0;
    		handbrake = 0;
    	}
    
    
    	//if car is on ground calculate handling, otherwise just rev the engine
     	if(onGround)
    		HandlePhysics();
    	else
    		CalcEngine();	
    		
    	//wheel GFX
    	UpdateWheels();
    
    	//engine sounds
    	audio.pitch=0.5+0.2*motor+0.8*engineRPM/maxRPM;
    	audio.volume=0.5+0.8*motor+0.2*engineRPM/maxRPM;
    }
    
    //Called by DamageReceiver if boat destroyed
    function Detonate()
    {
    	//destroy wheels
    	for( w in wheels )
    		w.coll.gameObject.active=false;
    
    	//no more car physics
    	enabled=false;
    }
    
    @script RequireComponent (Rigidbody)
    @script RequireComponent (AudioSource)
    
    • "Networking Scripts"フォルダにある、ConnectGuiMasterServer.jsを修正する

    Awake()メソッドのすぐしたへ

    var isConnectMyMasterServer = true;// falseだとUnity共有MasterServerに接続
    if( isConnectMyMasterServer ) {
    	MasterServer.ipAddress = "127.0.0.1";
    	MasterServer.port = 10000;
    }
    • ファイルを弄る
    HOMEDIR=[SrcRoot]	#実行ファイルが配置されているところ
    USERNAME=自分のPC名前
    OPTIONS="-s 3600 -p 10000 -l"
    • サーバ起動
    cd SrcRoot
    sudo config/unity-masterserver start

    Androidのために修正する
    →Pluginsフォルダを生成して、Networking ScriptsフォルダにもともとあったNetworkRigidbody.csとNetworkInterpolatedTransform.csを挿入する

    • Networking Scripts -> CarNetworkInit.js(修正)
    function OnNetworkInstantiate (msg : NetworkMessageInfo) {
    	// This is our own player
    	if (networkView.isMine)
    	{
    		Camera.main.SendMessage("SetTarget", transform);
    		(GetComponent("NetworkRigidbody") as NetworkRigidbody).enabled = false;
    	}
    	// This is just some remote controlled player, don't execute direct
    	// user input on this
    	else
    	{
    		name += "Remote";
    		GetComponent(Car).SetEnableUserInput(false);
    		(GetComponent("NetworkRigidbody") as NetworkRigidbody).enabled = true;
    	}
    }
    • Authoritative Server -> AuthServerSpawnPlayer.js
    var playerPrefab : Transform;
    // Local player information when one is instantiated
    private var localPlayer : NetworkPlayer;
    private var localTransformViewID : NetworkViewID;
    private var localAnimationViewID : NetworkViewID;
    private var isInstantiated : boolean = false;
    // The server uses this to track all intanticated player
    private var playerInfo : Array = new Array();
    
    class PlayerInfo {
    	var transformViewID : NetworkViewID;
    	var animationViewID : NetworkViewID;
    	var player : NetworkPlayer;
    }
    
    function OnGUI () {
    	if (Network.isClient && localPlayer.ToString() != 0 && !isInstantiated) 
    		if (GUI.Button(new Rect(20,Screen.height-60, 90, 20),"SpawnPlayer"))
    		{
    			// Spawn the player on all machines
    			networkView.RPC("SpawnPlayer", RPCMode.AllBuffered, localPlayer, localTransformViewID, localAnimationViewID);
    			isInstantiated = true;
    		}
    }
    
    // Receive server initialization, record own identifier as seen by the server.
    // This is later used to recognize if a network spawned player is the local player.
    // Also record assigned view IDs so the server can synch the player correctly.
    @RPC
    function InitPlayer (player : NetworkPlayer, tViewID : NetworkViewID, aViewID : NetworkViewID) {
    	Debug.Log("Received player init "+ player +". ViewIDs " + tViewID + " and " + aViewID);
    	localPlayer = player;
    	localTransformViewID = tViewID;
    	localAnimationViewID = aViewID;
    }
    
    // Create a networked player in the game. Instantiate a local copy of the player, set the view IDs
    // accordingly. 
    @RPC
    function SpawnPlayer (playerIdentifier : NetworkPlayer, transformViewID : NetworkViewID, animationViewID : NetworkViewID) {
    	Debug.Log("Instantiating player " + playerIdentifier);
    	var instantiatedPlayer : Transform = Instantiate(playerPrefab, transform.position, transform.rotation);
    	var networkViews = instantiatedPlayer.GetComponents(NetworkView);
    	
    	// Assign view IDs to player object
    	if (networkViews.Length != 2) {
    		Debug.Log("Error while spawning player, prefab should have 2 network views, has "+networkViews.Length);
    		return;
    	} else {
    		(networkViews[0] as NetworkView).viewID = transformViewID;
    		(networkViews[1] as NetworkView).viewID = animationViewID;
    	}
    	// Initialize local player
    	if (playerIdentifier == localPlayer) {
    		Debug.Log("Enabling user input as this is the local player");
    		// W are doing client prediction and thus enable the controller script + user input processing
    		instantiatedPlayer.GetComponent(ThirdPersonController).enabled = true;
    		instantiatedPlayer.GetComponent(ThirdPersonController).getUserInput = true;
    		// Enable input network synchronization (server gets input)
    		instantiatedPlayer.GetComponent(NetworkController).enabled = true;
    		instantiatedPlayer.SendMessage("SetOwnership", playerIdentifier);
    		return;
    	// Initialize player on server
    	} else if (Network.isServer) {
    		instantiatedPlayer.GetComponent(ThirdPersonController).enabled = true;
    		instantiatedPlayer.GetComponent(AuthServerPersonAnimation).enabled = true;
    		// Record player info so he can be destroyed properly
    		var playerInstance : PlayerInfo = new PlayerInfo();
    		playerInstance.transformViewID = transformViewID;
    		playerInstance.animationViewID = animationViewID;
    		playerInstance.player = playerIdentifier;
    		playerInfo.Add(playerInstance);
    		Debug.Log("There are now " + playerInfo.length + " players active");
    	}
    }
    
    // This runs if the scene is executed from the loader scene.
    // Here we must check if we already have clients connect which must be reinitialized.
    // This is the same procedure as in OnPlayerConnected except we process already
    // connected players instead of new ones. The already connected players have also
    // reloaded the level and thus have a clean slate.
    function OnNetworkLoadedLevel() {
    	if (Network.isServer && Network.connections.Length > 0) {
    		for (var p : NetworkPlayer in Network.connections) {
    			Debug.Log("Resending player init to "+p);
    			var transformViewID : NetworkViewID = Network.AllocateViewID();
    			var	animationViewID : NetworkViewID = Network.AllocateViewID();
    			Debug.Log("Player given view IDs "+ transformViewID + " and " + animationViewID);
    			networkView.RPC("InitPlayer", p, p, transformViewID, animationViewID);
    		}
    	}
    }
    
    // Send initalization info to the new player, before that he cannot spawn himself
    function OnPlayerConnected (player : NetworkPlayer) {
    	Debug.Log("Sending player init to "+player);
    	var transformViewID : NetworkViewID = Network.AllocateViewID();
    	var	animationViewID : NetworkViewID = Network.AllocateViewID();
    	Debug.Log("Player given view IDs "+ transformViewID + " and " + animationViewID);
    	networkView.RPC("InitPlayer", player, player, transformViewID, animationViewID);
    }
    
    function OnPlayerDisconnected (player : NetworkPlayer) {
    	Debug.Log("Cleaning up player " + player);
    	// Destroy the player object this network player spawned
    	var deletePlayer : PlayerInfo;
    	for (var playerInstance : PlayerInfo in playerInfo) {
    		if (player == playerInstance.player) {
    			Debug.Log("Destroying objects belonging to view ID " + playerInstance.transformViewID);
    			Network.Destroy(playerInstance.transformViewID);
    			deletePlayer = playerInstance;
    		}
    	}
    	playerInfo.Remove(deletePlayer);
    	Network.RemoveRPCs(player, 0);
    	Network.DestroyPlayerObjects(player);
    }
    • Vehicles -> SurfaceEffects -> Tracks -> Skidmarks.js(修正する)
    -//Script used by the car Script to create skidmark meshes when cornering.
    //Just create an empty GameObject and attach this.
    RequireComponent(MeshFilter);
    RequireComponent(MeshRenderer);
    
    //maximal number of skidmarks 
    var maxMarks = 512;
    
    //width of skid marks
    var markWidth = 0.225;
    
    //time interval new mesh segments are generated in
    //the lower this value, the smoother the generated tracks
    var updateRate = 0.1;
    	
    private var indexShift = 0;
    private var numMarks = 0;
    private var updateTime = 0.0;	
    private var newTrackFlag = true;
    private var updateMeshFlag = true;
    
    //data structure describing a section of skidmarks
    class markSection{
    	var pos : Vector3;
    	var normal : Vector3;
    	var posl : Vector3;
    	var posr : Vector3;
    	var intensity = 0.0;
    	var lastIndex = -1;
    };
    
    var skidmarks : markSection[];	
    	
    // Use this for initialization
    function Start () {
    	//create structures
    	skidmarks=new markSection[maxMarks];
    	for(var i:int=0;i<maxMarks;i++)
    		skidmarks[i]=new markSection();
    	if(GetComponent(MeshFilter).mesh==null)
    		GetComponent(MeshFilter).mesh=new Mesh();
    }
    	
    //called by the car script to add a skid mark at position pos with the supplied normal.
    //transparency can be specified in the intensity parameter. Connects to the track segment 
    //indexed by lastIndex (or it won't display if lastIndex is -1). returns an index value 
    //which can be passed as lastIndex to the next AddSkidMark call
    function AddSkidMark(pos : Vector3, normal : Vector3,intensity : float,lastIndex : int) : int
    {
    	intensity=Mathf.Clamp01(intensity);
    	
    	//get index for new segment
    	var currIndex:int=numMarks;
    	
    	//reuse lastIndex if we don't need to create a new one this frame
    	if(lastIndex!=-1 && !newTrackFlag)
    		currIndex=lastIndex;
    		
    	//setup skidmark structure
    	var curr=skidmarks[currIndex%maxMarks];
    	curr.pos=pos+normal*0.05-transform.position;
    	curr.normal=normal;
    	curr.intensity=intensity;
    	
    	if(lastIndex==-1 || newTrackFlag)
    		curr.lastIndex=lastIndex;
    		
    	//if we have a valid lastIndex, get positions for marks
    	if(curr.lastIndex!=-1)
    	{
    		var last = skidmarks[curr.lastIndex%maxMarks];
    		var dir = (curr.pos-last.pos);
    		var xDir : Vector3 = Vector3.Cross(dir,normal).normalized;
    		
    		curr.posl=curr.pos+xDir*markWidth*0.5;
    		curr.posr=curr.pos-xDir*markWidth*0.5;
    		
    		if(last.lastIndex==-1)
    		{
    			last.posl=curr.pos+xDir*markWidth*0.5;
    			last.posr=curr.pos-xDir*markWidth*0.5;
    		}
    	}
    	if(lastIndex==-1 || newTrackFlag)
    		numMarks++;
    	updateMeshFlag=true;
    	return currIndex;
    }
    
    //regenerate the skidmarks mesh	
    function UpdateMesh () {
    	//count visible segments
    	var segmentCount:int = 0;
    	for(var i:int=0;i<numMarks&&i<maxMarks;i++)
    		if(skidmarks[i].lastIndex!=-1&&skidmarks[i].lastIndex>numMarks-maxMarks)
    			segmentCount++;
    	
    	//create skidmark mesh coordinates
    	var vertices=new Vector3[segmentCount*4];
    	var normals=new Vector3[segmentCount*4];
    	var colors=new Color[segmentCount*4];
    	var uvs = new Vector2[segmentCount*4];
    	var triangles=new int[segmentCount*6];
    	segmentCount = 0;
    	for (i=0;i<numMarks&&i<maxMarks;i++)
    		if(skidmarks[i].lastIndex!=-1&&skidmarks[i].lastIndex>numMarks-maxMarks)
    		{
    			var curr=skidmarks[i];
    			var last = skidmarks[curr.lastIndex%maxMarks];
    						
    			vertices[segmentCount*4+0]=last.posl;
    			vertices[segmentCount*4+1]=last.posr;
    			vertices[segmentCount*4+2]=curr.posl;
    			vertices[segmentCount*4+3]=curr.posr;
    			
    			normals[segmentCount*4+0]=last.normal;
    			normals[segmentCount*4+1]=last.normal;
    			normals[segmentCount*4+2]=curr.normal;
    			normals[segmentCount*4+3]=curr.normal;
    
    			colors[segmentCount*4+0]=new Color(1,1,1,last.intensity);
    			colors[segmentCount*4+1]=new Color(1,1,1,last.intensity);
    			colors[segmentCount*4+2]=new Color(1,1,1,curr.intensity);
    			colors[segmentCount*4+3]=new Color(1,1,1,curr.intensity);
    
    			uvs[segmentCount*4+0]=new Vector2(0,0);
    			uvs[segmentCount*4+1]=new Vector2(1,0);
    			uvs[segmentCount*4+2]=new Vector2(0,0);
    			uvs[segmentCount*4+3]=new Vector2(1,0);
    			
    			triangles[segmentCount*6+0]=segmentCount*4+0;
    			triangles[segmentCount*6+1]=segmentCount*4+1;
    			triangles[segmentCount*6+2]=segmentCount*4+2;
    			
    			triangles[segmentCount*6+3]=segmentCount*4+2;
    			triangles[segmentCount*6+4]=segmentCount*4+1;
    			triangles[segmentCount*6+5]=segmentCount*4+3;
    			segmentCount++;			
    		}
    	
    	//update mesh
    	var mesh = GetComponent(MeshFilter).mesh;
    	mesh.Clear();
    	mesh.vertices=vertices;
    	mesh.normals=normals;
    	mesh.triangles=triangles;
    	mesh.colors=colors;
    	mesh.uv=uvs;
    	updateMeshFlag=false;
    }
    
    function Update()
    {
    	//update mesh if skidmarks have changed since last frame
    	if(updateMeshFlag)
    		UpdateMesh();
    }
    
    function FixedUpdate()
    {
    	//set flag for creating new segments this frame if an update is pending
    	newTrackFlag = false;
    	updateTime += Time.deltaTime;
    	if(updateTime > updateRate)
    	{
    		newTrackFlag = true;
    		updateTime -= updateRate;
    	}
    }
    • Networking Scripts -> ConnectGuiMasterServer.js(修正する)
    DontDestroyOnLoad(this);
    
    var gameName = "You must change this";
    var serverPort = 25002;
    
    private var timeoutHostList = 0.0;
    private var lastHostListRequest = -1000.0;
    private var hostListRefreshTimeout = 10.0;
    
    private var connectionTestResult : ConnectionTesterStatus = ConnectionTesterStatus.Undetermined;
    private var filterNATHosts = false;
    private var probingPublicIP = false;
    private var doneTesting = false;
    private var timer : float = 0.0;
    private var useNat = false;		// Should the server enabled NAT punchthrough feature
    
    private var windowRect : Rect;
    private var serverListRect;
    private var hideTest = false;
    private var testMessage = "Undetermined NAT capabilities";
    
    // Enable this if not running a client on the server machine
    //MasterServer.dedicatedServer = true;
    
    function OnFailedToConnectToMasterServer(info: NetworkConnectionError)
    {
    	Debug.Log(info);
    }
    
    function OnFailedToConnect(info: NetworkConnectionError)
    {
    	Debug.Log(info);
    }
    
    function OnGUI ()
    {
    	windowRect = GUILayout.Window (0, windowRect, MakeWindow, "Server Controls");
    	if (Network.peerType == NetworkPeerType.Disconnected && MasterServer.PollHostList().Length != 0)
    		serverListRect = GUILayout.Window(1, serverListRect, MakeClientWindow, "Server List");
    }
    
    function Awake ()
    {
    	
    	var isConnectMyMasterServer = true;//falseにするとUnity共有MasterServerに接続
    	if( isConnectMyMasterServer ) {
    		MasterServer.ipAddress = "127.0.0.1";
    		MasterServer.port = 10000;
    	}
    	
    
    	windowRect = Rect(Screen.width-300,0,300,100);
    	serverListRect = Rect(0, 0, Screen.width - windowRect.width, 100);
    	// Start connection test
    	connectionTestResult = Network.TestConnection();
    	
    	// What kind of IP does this machine have? TestConnection also indicates this in the
    	// test results
    	if (Network.HavePublicAddress())
    		Debug.Log("This machine has a public IP address");
    	else
    		Debug.Log("This machine has a private IP address");
    }
    
    function Update()
    {
    	// If test is undetermined, keep running
    	if (!doneTesting)
    		TestConnection();
    }
    
    function TestConnection()
    {
    	// Start/Poll the connection test, report the results in a label and react to the results accordingly
    	connectionTestResult = Network.TestConnection();
    	switch (connectionTestResult)
    	{
    		case ConnectionTesterStatus.Error: 
    			testMessage = "Problem determining NAT capabilities";
    			doneTesting = true;
    			break;
    			
    		case ConnectionTesterStatus.Undetermined: 
    			testMessage = "Undetermined NAT capabilities";
    			doneTesting = false;
    			break;
    						
    		case ConnectionTesterStatus.PublicIPIsConnectable:
    			testMessage = "Directly connectable public IP address.";
    			useNat = false;
    			doneTesting = true;
    			break;
    			
    		// This case is a bit special as we now need to check if we can 
    		// circumvent the blocking by using NAT punchthrough
    		case ConnectionTesterStatus.PublicIPPortBlocked:
    			testMessage = "Non-connectble public IP address (port " + serverPort +" blocked), running a server is impossible.";
    			useNat = false;
    			// If no NAT punchthrough test has been performed on this public IP, force a test
    			if (!probingPublicIP)
    			{
    				Debug.Log("Testing if firewall can be circumvented");
    				connectionTestResult = Network.TestConnectionNAT();
    				probingPublicIP = true;
    				timer = Time.time + 10;
    			}
    			// NAT punchthrough test was performed but we still get blocked
    			else if (Time.time > timer)
    			{
    				probingPublicIP = false; 		// reset
    				useNat = true;
    				doneTesting = true;
    			}
    			break;
    		case ConnectionTesterStatus.PublicIPNoServerStarted:
    			testMessage = "Public IP address but server not initialized, it must be started to check server accessibility. Restart connection test when ready.";
    			break;
    			
    		case ConnectionTesterStatus.LimitedNATPunchthroughPortRestricted:
    			Debug.Log("LimitedNATPunchthroughPortRestricted");
    			testMessage = "Limited NAT punchthrough capabilities. Cannot connect to all types of NAT servers.";
    			useNat = true;
    			doneTesting = true;
    			break;
    					
    		case ConnectionTesterStatus.LimitedNATPunchthroughSymmetric:
    			Debug.Log("LimitedNATPunchthroughSymmetric");
    			testMessage = "Limited NAT punchthrough capabilities. Cannot connect to all types of NAT servers. Running a server is ill adviced as not everyone can connect.";
    			useNat = true;
    			doneTesting = true;
    			break;
    		
    		case ConnectionTesterStatus.NATpunchthroughAddressRestrictedCone:
    		case ConnectionTesterStatus.NATpunchthroughFullCone:
    			Debug.Log("NATpunchthroughAddressRestrictedCone || NATpunchthroughFullCone");
    			testMessage = "NAT punchthrough capable. Can connect to all servers and receive connections from all clients. Enabling NAT punchthrough functionality.";
    			useNat = true;
    			doneTesting = true;
    			break;
    
    		default: 
    			testMessage = "Error in test routine, got " + connectionTestResult;
    	}
    	//Debug.Log(connectionTestResult + " " + probingPublicIP + " " + doneTesting);
    }
    
    function MakeWindow (id : int)
    {	
    	hideTest = GUILayout.Toggle(hideTest, "Hide test info");
    	
    	if (!hideTest)
    	{
    		GUILayout.Label(testMessage);
    		if (GUILayout.Button ("Retest connection"))
    		{
    			Debug.Log("Redoing connection test");
    			probingPublicIP = false;
    			doneTesting = false;
    			connectionTestResult = Network.TestConnection(true);
    		}
    	}
    	
    	if (Network.peerType == NetworkPeerType.Disconnected)
    	{
    		GUILayout.BeginHorizontal();
    		GUILayout.Space(10);
    		// Start a new server
    		if (GUILayout.Button ("Start Server"))
    		{
    			Network.InitializeServer(32, serverPort, useNat);
    			MasterServer.RegisterHost(gameName, "stuff", "l33t game for all");
    		}
    
    		// Refresh hosts
    		if (GUILayout.Button ("Refresh available Servers") || Time.realtimeSinceStartup > lastHostListRequest + hostListRefreshTimeout)
    		{
    			MasterServer.RequestHostList (gameName);
    			lastHostListRequest = Time.realtimeSinceStartup;
    		}
    		
    		GUILayout.FlexibleSpace();
    		
    		GUILayout.EndHorizontal();
    	}
    	else
    	{
    		if (GUILayout.Button ("Disconnect"))
    		{
    			Network.Disconnect();
    			MasterServer.UnregisterHost();
    		}
    		GUILayout.FlexibleSpace();
    	}
    	GUI.DragWindow (Rect (0,0,1000,1000));
    }
    
    function MakeClientWindow(id : int)
    {
    	GUILayout.Space(5);
    
    	var data : HostData[] = MasterServer.PollHostList();
    	var count = 0;
    	for (var element in data)
    	{
    		GUILayout.BeginHorizontal();
    
    		// Do not display NAT enabled games if we cannot do NAT punchthrough
    		if ( !(filterNATHosts && element.useNat) )
    		{
    			var connections = element.connectedPlayers + "/" + element.playerLimit;
    			GUILayout.Label(element.gameName);
    			GUILayout.Space(5);
    			GUILayout.Label(connections);
    			GUILayout.Space(5);
    			var hostInfo = "";
    			
    			// Indicate if NAT punchthrough will be performed, omit showing GUID
    			if (element.useNat)
    			{
    				GUILayout.Label("NAT");
    				GUILayout.Space(5);
    			}
    			// Here we display all IP addresses, there can be multiple in cases where
    			// internal LAN connections are being attempted. In the GUI we could just display
    			// the first one in order not confuse the end user, but internally Unity will
    			// do a connection check on all IP addresses in the element.ip list, and connect to the
    			// first valid one.
    			for (var host in element.ip)
    				hostInfo = hostInfo + host + ":" + element.port + " ";
    			
    			//GUILayout.Label("[" + element.ip + ":" + element.port + "]");	
    			GUILayout.Label(hostInfo);	
    			GUILayout.Space(5);
    			GUILayout.Label(element.comment);
    			GUILayout.Space(5);
    			GUILayout.FlexibleSpace();
    			if (GUILayout.Button("Connect"))
    				Network.Connect(element);
    		}
    		GUILayout.EndHorizontal();	
    	}
    }
    
    • Castles ->CastlesNetworkInit.js
    var castlePrefab : Transform;
    var playerPrefab : Transform;
    
    private var invisiblePlayer : Object;
    
    // Server variables
    var maxPlayers = 1;
    private var playerCount : int = 0;
    
    // The playerList is used by both server and client, the client sets his offical server approved info here.
    var playerList : Array = new Array();
    
    // Local player credentials (his profile)
    public var playerName : String = "Testing";
    public var playerColor : Color = Color.yellow;
    
    private var isInitialized : boolean = false;
    private var gameStarted : boolean = false;
    
    private var playerWindow = Rect (10,60,110,50);
    
    private var serverWindow = Rect (130,60,110,50);
    
    private var colorSelection : String[] = ["Green", "Yellow", "Black", "Grey"];
    private var selectedColor : int;
    private var playerNumbers : String[] = ["2", "3", "4"];
    
    class CastlePlayer {
    	var number : int;
    	var color : Color;
    	var name : String;
    	var player : NetworkPlayer;
    	
    	function ToString() : String {
    		return name + " " + number + " " + color + " " + player;
    	}
    }
    
    function OnGUI() {
    	if (isInitialized) {
    		GUI.Label(new Rect(Screen.width-150, 20, 140, 50), "Player name: "+ (playerList[0] as CastlePlayer).name + "\nPlayer number: " + (playerList[0] as CastlePlayer).number);
    	}
    	else {
    		playerWindow = GUILayout.Window(1, playerWindow, MakePlayerWindow, "Player Info");
    		serverWindow = GUILayout.Window(2, serverWindow, MakeServerWindow, "Server Info");
    	}
    	if (isInitialized && !gameStarted) {
    		GUI.contentColor = Color.blue;
    		GUI.Label(new Rect(20, Screen.height-60, 250, 20), "Ghost mode, waiting for more players");
    	}
    }
    
    function MakePlayerWindow(id : int) {
    	GUILayout.Label("Name:");
    	playerName = GUILayout.TextField(playerName);
    	GUILayout.Label("Color:");
    	selectedColor = GUILayout.SelectionGrid(selectedColor, colorSelection, 1);
    	switch (selectedColor) {
    		case 0: playerColor = Color.green; break;
    		case 1: playerColor = Color.yellow; break;
    		case 2: playerColor = Color.black; break;
    		case 3: playerColor = Color.grey; break;
    		default: playerColor = Color.blue; break;
    	}
    }
    
    function MakeServerWindow(id : int) {
    	GUILayout.Label("Max Players:");
    	var tmp = GUILayout.SelectionGrid(maxPlayers-2, playerNumbers, 3);
    	maxPlayers = tmp + 2;
    }
    function OnNetworkLoadedLevel ()
    {
    	// Start invisible floating body camera
    	invisiblePlayer = Instantiate(playerPrefab, new Vector3(0,2,0), Quaternion.identity);
    	Camera.main.SendMessage("SetTarget", invisiblePlayer);
    	
    	if (Network.isServer && !isInitialized) {
    		// Doing server/client now
    		Network.maxConnections = maxPlayers-1;
    		var playerInfo = new CastlePlayer();
    		playerInfo.number = playerCount++;
    		playerInfo.name = playerName;
    		playerInfo.color = playerColor;
    		// Add self to list
    		playerList.Add(playerInfo);
    		isInitialized = true;
    	}
    	
    	// If I'm a client and I'm already connected to a server, then re-request player entry to game
    	if (Network.isClient && Network.connections.Length > 0) {
    		OnConnectedToServer();
    	}
    }
    
    @RPC
    function ReceivePlayer (number: int, r: float, g: float, b: float, name: String, player: NetworkPlayer) {
    	var somePlayer : CastlePlayer = new CastlePlayer();
    	somePlayer.number = number;
    	somePlayer.color = new Color(r,g,b);
    	somePlayer.name = name;
    	somePlayer.player = player;
    	playerList.Add(somePlayer);
    	Debug.Log("Received info on player: " + somePlayer);
    }
    
    // Server uses this to tell the client his network player is approved and ready to join
    @RPC
    function InitializeClient (player: NetworkPlayer, number: int, r: float, g: float, b: float) {
    	Debug.Log("Client initialized with number "+number);
    	
    	var localPlayer : CastlePlayer = new CastlePlayer();
    	localPlayer.player = player;
    	localPlayer.name = playerName;
    	localPlayer.number = number;
    	localPlayer.color = new Color(r,g,b);
    	playerList.Add(localPlayer);
    	isInitialized = true;
    }
    
    // Client uses this to announce himself and his desired credentials (like color) to the server
    @RPC
    function RequestPlayer (name: String, r: float, g: float, b: float, info: NetworkMessageInfo) {
    	var newColor = new Color(r,g,b);
    	// Check if color is already used
    	var available : boolean = true;
    	for (var p : CastlePlayer in playerList) {
    		if (newColor == p.color)
    			available = false;
    	}
    	// Create new player in list
    	var playerInfo = new CastlePlayer();
    	playerInfo.player = info.sender;
    
    	// Initilize the rest of the variables
    	playerInfo.number = playerCount++;
    	if (available)
    		playerInfo.color = newColor;
    	else {
    		Debug.Log("Requested color already in use, giving player default color");
    		playerInfo.color = Color.green;
    	}
    	playerInfo.name = name;
    
    	playerList.Add(playerInfo);
    
    	// Send init message to client, confirming he is in the game and his parameters are initialized
    	networkView.RPC("InitializeClient", info.sender, info.sender, playerInfo.number, playerInfo.color.r, playerInfo.color.g, playerInfo.color.b);
    	
    	// If all players have arrived, start the game
    	if (Network.isServer && playerCount == maxPlayers)
    	{
    		// Send player list to clients
    		for (var receiver : CastlePlayer in playerList) {
    			for (var playerInfo : CastlePlayer in playerList) {
    				if (receiver.player != playerInfo.player && receiver.player.ToString() != "0") {
    					Debug.Log("Sending info on player " + playerInfo.player + " to player " + receiver.player);
    					networkView.RPC("ReceivePlayer", receiver.player, playerInfo.number, playerInfo.color.r, playerInfo.color.g, playerInfo.color.b, playerInfo.name, playerInfo.player);
    				}
    			}
    		}
    		Debug.Log ("All " + playerCount + " player(s) connected, starting game.");
    		networkView.RPC("OnGameReady", RPCMode.All);
    	}
    }
    
    // Run everywhere when all players have connected, also runs on server as he is also a client
    @RPC
    function OnGameReady ()
    {
    	Debug.Log ("Starting game with player number " + (playerList[0] as CastlePlayer).number);
    
    	var viewID : NetworkViewID = Network.AllocateViewID();
    	switch ((playerList[0] as CastlePlayer).number)
    	{
    		case 0:
    			networkView.RPC("SpawnCastle", RPCMode.AllBuffered, viewID, new Vector3(-40,1,-40), (playerList[0] as CastlePlayer).number);
    			Camera.main.transform.position = new Vector3(-45,5,-45);
    			break;
    		case 1:
    			networkView.RPC("SpawnCastle", RPCMode.AllBuffered, viewID, new Vector3(-40,1, 40), (playerList[0] as CastlePlayer).number);
    			Camera.main.transform.position = new Vector3(-45,5,45);
    			break;
    		case 2: 
    			networkView.RPC("SpawnCastle", RPCMode.AllBuffered, viewID, new Vector3(40,1, -40), (playerList[0] as CastlePlayer).number);
    			Camera.main.transform.position = new Vector3(45,5,-45);
    			break;
    		case 3: 
    			networkView.RPC("SpawnCastle", RPCMode.AllBuffered, viewID, new Vector3( 40,1, 40), (playerList[0] as CastlePlayer).number);
    			Camera.main.transform.position = new Vector3(45,5,45);
    			break;
    		default: 
    			Debug.Log ("Invalid player number given during game start: " + (playerList[0] as CastlePlayer).number);
    	}
    	gameStarted = true;
    	
    	// Destroy invisible player
    	Destroy((invisiblePlayer as GameObject).gameObject);
    }
    
    function GetColor(playerNumber: int) : Color {
    	for (var p : CastlePlayer in playerList) {
    		if (p.number == playerNumber)
    			return p.color;
    	}
    	Debug.Log("Couldn't find color for player " + playerNumber);
    	return Color.red;
    }
    
    @RPC
    function SpawnCastle(viewID: NetworkViewID, position: Vector3, number: int) {
    	var castle: Object = Instantiate(castlePrefab, position, transform.rotation);
    
    	(castle as GameObject).GetComponent(NetworkView).viewID = viewID;
    
    	if (!(castle as GameObject).GetComponent(NetworkView).isMine) {
    		(castle as GameObject).name += "Remote";
    		Debug.Log("Looking up color for player " +number);
    		(castle  as GameObject).renderer.material.color = GetColor(number);
    	}
    	else {
    		(castle as GameObject).renderer.material.color = (playerList[0] as CastlePlayer).color;
    		Debug.Log("Instantiated own castle");
    	}
    }
    
    // When connected to server, request to get local player initialized in the game
    function OnConnectedToServer() {
    	networkView.RPC("RequestPlayer", RPCMode.Server, playerName, playerColor.r, playerColor.g, playerColor.b);
    }
    
    // Process each player when he connects to the on the server. Add to player list.
    function OnPlayerConnected (player : NetworkPlayer)
    {
    	Debug.Log ("Server: player " + player.ToString() + " connected");
    	if (gameStarted) {
    		Debug.Log("Rejecting new player as the game is already started");
    		Network.CloseConnection(player, true);
    	}
    }
    
    function OnPlayerDisconnected (player : NetworkPlayer)
    {
    	Network.RemoveRPCs(player, 0);
    	Network.DestroyPlayerObjects(player);
    }
    • Networking Scripts -> ThirdPersonNetworkInit.js(修正する)
    function OnNetworkInstantiate (msg : NetworkMessageInfo) {
    	// This is our own player
    	if (networkView.isMine)
    	{
    		Camera.main.SendMessage("SetTarget", transform);
    		(GetComponent("NetworkInterpolatedTransform") as NetworkInterpolatedTransform).enabled = false;
    	}
    	// This is just some remote controlled player
    	else
    	{
    		name += "Remote";
    		GetComponent(ThirdPersonController).enabled = false;
    		GetComponent(ThirdPersonSimpleAnimation).enabled = false;
    		(GetComponent("NetworkInterpolatedTransform") as NetworkInterpolatedTransform).enabled = true;
    	}
    }
    • Vehicles->SurfaceEffects->Spume->WaterSpume.js(Androidではバグるので修正)
    -//Attach this script to an object to create a foam layer on top 
    //of the attached trigger collider, which automatically creates foam whenever anything
    //touches the trigger.
    
    RequireComponent(BoxCollider);
    RequireComponent(MeshFilter);
    RequireComponent(MeshRenderer);
    
    //maximal number of simultaneous foam particles
    var maxParticles = 1024;
    //maximal seconds of life for one particle
    var maxLife = 16.0;	
    
    private var index = 0;
    
    //structure for each particle
    class FoamParticle{
    	var pos : Vector3;
    	var velo : Vector3;
    	var ext : Vector3;
    	var intensity = 0.0;
    	var startIntensity =0.0;
    };
    var foam : FoamParticle[];	
    	
    // Use this for initialization
    function Start () {
    	collider.isTrigger = true;
    
    	//create particle array
    	foam=new FoamParticle[maxParticles];
    	for(var i:int =0;i<maxParticles;i++)
    		foam[i]=new FoamParticle();
    		
    	//create particle mesh
    	if(GetComponent(MeshFilter).mesh==null)
    		GetComponent(MeshFilter).mesh=new Mesh();
    }
    	
    //add new foam particles in the extents ext around pos, with the maximal life of intensity
    function AddFoamParticles(pos : Vector3,ext: Vector3,intensity : float)
    {
    	//flatten coordinates to all be on the water surface
    	pos-=transform.position;
    	pos.y=0;
    	ext.y=0;
    	
    	intensity=Mathf.Clamp01(intensity);
    
    	//determine number of particles based on ext and intensity
    	var sizeFactor = Mathf.Sqrt(ext.magnitude);
    	if(sizeFactor<1)
    		intensity*=sizeFactor;
    	var fnum = intensity*sizeFactor*Time.deltaTime*50;
    	var num:int;
    	num = Mathf.Floor(fnum);
    	num+=(fnum-num)>Random.value?1:0;
    		
    	//create particles
    	while(num>0)
    	{
    		var rnd = Random.insideUnitSphere;
    		rnd.y = 0;
    		foam[index].pos=pos+Vector3.Scale(rnd,ext);
    		foam[index].velo=rnd*intensity;
    		foam[index].intensity=Random.value*intensity;
    		foam[index].startIntensity=foam[index].intensity;
    		foam[index].ext=Random.onUnitSphere*(1.0+1.5*Random.value)*sizeFactor;
    		foam[index].ext.y=0;
    		index=(index+1)%maxParticles;
    		num--;
    	}
    }
    
    	
    function Update () {
    	//count and update active particles
    	var segmentCount = 0;
    	for(var i=0;i<maxParticles;i++)
    	{
    		//decrease life
    		foam[i].intensity-=Time.deltaTime/maxLife;
    		if(foam[i].intensity>0)
    		{
    			//move particles
    			foam[i].pos+=foam[i].velo*Time.deltaTime;
    			//grow particles
    			var size = foam[i].ext.magnitude;
    			foam[i].ext*=1+(Time.deltaTime*0.5/size);
    			//increase count of active particles
    			segmentCount++;
    		}
    	}	
    	//calculate mesh data for particles
    	var vertices = new Vector3[segmentCount*4];
    	var colors = new Color[segmentCount*4];
    	var uvs = new Vector2[segmentCount*4];
    	var triangles = new int[segmentCount*6];
    	segmentCount = 0;
    	for(i=0;i<maxParticles;i++)
    		if(foam[i].intensity>0)
    		{
    			var ext1=foam[i].ext;
    			var ext2=Vector3(ext1.z,0,-ext1.x);
    			vertices[segmentCount*4+0]=foam[i].pos+ext1;
    			vertices[segmentCount*4+1]=foam[i].pos+ext2;
    			vertices[segmentCount*4+2]=foam[i].pos-ext1;
    			vertices[segmentCount*4+3]=foam[i].pos-ext2;
    			
    			var intensity=foam[i].intensity;
    			if(foam[i].startIntensity-intensity<0.03)
    				intensity*=((foam[i].startIntensity-intensity)/0.03);
    			colors[segmentCount*4+0]=0.5*Color(intensity,intensity,intensity,1);
    			colors[segmentCount*4+1]=0.5*Color(intensity,intensity,intensity,1);
    			colors[segmentCount*4+2]=0.5*Color(intensity,intensity,intensity,1);
    			colors[segmentCount*4+3]=0.5*Color(intensity,intensity,intensity,1);
    
    			uvs[segmentCount*4+0]=Vector2(0,0);
    			uvs[segmentCount*4+1]=Vector2(1,0);
    			uvs[segmentCount*4+2]=Vector2(1,1);
    			uvs[segmentCount*4+3]=Vector2(0,1);
    			
    			triangles[segmentCount*6+0]=segmentCount*4+0;
    			triangles[segmentCount*6+1]=segmentCount*4+1;
    			triangles[segmentCount*6+2]=segmentCount*4+2;
    			
    			triangles[segmentCount*6+3]=segmentCount*4+2;
    			triangles[segmentCount*6+4]=segmentCount*4+0;
    			triangles[segmentCount*6+5]=segmentCount*4+3;
    			segmentCount++;			
    		}
    		
    	//update mesh
    	var mesh = GetComponent(MeshFilter).mesh;
    	mesh.Clear();
    	mesh.vertices=vertices;
    	mesh.triangles=triangles;
    	mesh.colors=colors;
    	mesh.uv=uvs;
    }
    
    //get the rigidbody attached to a component's gameobject or it's anchestors
    function GetComponentRigidBody(cmp : GameObject) : Rigidbody {
    	if (cmp.rigidbody!=null)
    		return cmp.rigidbody;
    	else if (cmp.transform.parent!= null)
    		return (GetComponentRigidBody((cmp.transform.parent as GameObject)) as Rigidbody);
    	else
    		return null;
    }
    
    //if something fell into the water...
    function OnTriggerStay(other : Collider) {
    	var rb : Rigidbody = GetComponentRigidBody(other as GameObject);
    	//...and it has a rigidbody...
    	if (rb!=null) {
    		//...create foam particles around the object, based on the velocity of the rigidbody
    		var pos = other.transform.position;
    		AddFoamParticles(pos, (other as Renderer).bounds.extents, rb.velocity.magnitude * 0.05);
    	}
    }

    カスタムインスペクター Unity

    http://www.unifycommunity.com/wiki/index.php?title=Notes
    →ここを参考に使ってみた。
    Hierarchy上のGameObjectたちのInspector上にメモ書きできる

    • Note.cs・・・参考HPにあるのC#スクリプトを記載し、好きな場所に配置。
    • NoteEditor.cs・・・同WebページのC#スクリプトを記載し、Assets->Editorに配置(適当に配置するとエラー出る)

    Note.csはメモ書きされたいGameObjectへ適用する


    AddComponentMenu Unity

    カットシステム本のP107

    説明がわかりにくいが、よく使う自分の書いたスクリプトを、
    ”Unityの「Component」メニュー上に表示させたい”ときに使います。
    クラス名の”上”に記載します!

    今回は
    [AddComponentMenu("Usefultool/AutoTilling")]
    このように記載した
    こうするとUnityの「Component」メニューの「Usefultool->AutoTilling」へ追加されます。スラッシュでメニューの親子関係を成立してます。

    今度からこれを選択するだけで適用されます。

    記載する「AutoTilling」は今回は便宜上一所ですが、スクリプトの名前と”違ってもOK”です。

    using UnityEngine;
    using System.Collections;
    
    [AddComponentMenu("Usefultool/AutoTilling")]
    public class AutoTiling : MonoBehaviour {
    	public float ScaleToTiles = 0.667F;
    	private float scaleX;
    	private float scaleY;
    	
    	// Use this for initialization
    	void Start () {
    		scaleX = transform.lossyScale.x * ScaleToTiles;
            scaleY = transform.lossyScale.y * ScaleToTiles;
    		renderer.material.mainTextureScale = new Vector2(scaleX, scaleY);
    	}
    }
    

    このコードを任意のフォルダに「AutoTiling.cs」とかで使う