タグ別アーカイブ: javascript

[JS]GoogleDocsの画像をgithubに貼付けるための直リンク生成bookmarkletを作った

githubにはwikiを記述できるのだけど、wikiに記述する画像はuploadできない。
dropbox等のローカルにファイルが占有されることがないかたちで共有するにはどうしたらいいか調べ所、google drive上のimageから直リンクが取れるという事がわかった。
Permalinks for Google Drive Images

手順

  1. google drive に共有用フォルダを作成する。
  2. 画像をup
  3. google drive 上にある画像を右クリックで 開く->ドライブビューワ で開く。
  4. URLを先のリンクに従って変更する
  5. ねんがんの 直リンクURI をてにいれたぞ

ここで4のURLを毎度変更するのが面倒だったのでブックマークレットを作成した。
続きを読む

Photoshopで iOS App リリースに必要なアイコンを出力するスクリプト

iOSのアプリをリリースするにあたっていくつかのアイコン等が必要になります。
いちいちリサイズするのは面倒なので出力を自動化するスクリプトを作成しました。

やってること

  1. ドキュメントを新規作成。サイズは1024^2 (pix)
  2. リサイズしながら必要サイズのアイコン画像を指定の名前で保存。

サンプルと使い方

1024^2の解像度でオリジナルのアイコンを作成します。
アイコンを作成したらスクリプトを実行します。
以下のスクリプトをxx.jsxという名前で保存し、File->Script->Brows…から読み込むと実行されます。
保存先は最初に作ったアイコンが保存されているディレクトリなので、オリジナルpsdデータを保存しておく必要があります。
保存してないとエラーがでます。エラーの対処はしてません。

preferences.rulerUnits = Units.POINTS;
var saveName = activeDocument.path;
var doc;
createNewDocument();
savePngImgWithName("iTunesArtwork@2x");
changeImgSizeTo(512);
savePngImgWithName("iTunesArtwork");
changeImgSizeTo(144);
savePngImgWithName("Icon-72@2x");
changeImgSizeTo(114);
savePngImgWithName("icon@2x");	
changeImgSizeTo(72);
savePngImgWithName("Icon-72");
changeImgSizeTo(57);
savePngImgWithName("icon");
activeDocument.close(SaveOptions.DONOTSAVECHANGES);

function createNewDocument()
{
	doc = activeDocument.duplicate();
	w = activeDocument.width;
	h = activeDocument.height;
//	alert ( w+","+h );
	if( w != 1024 || h != 1024)
	{
		changeImgSizeTo(1024);
	}
}
function savePngImgWithName(name)
{
	var pathAndName = saveName +"/"+name+".png";
	var fileObj = new File(pathAndName);
	pngOpt = new PNGSaveOptions();
	pngOpt.interlaced = false;
	activeDocument.saveAs(fileObj,pngOpt,true, Extension.LOWERCASE);
}
function changeImgSizeTo(size)
{
	activeDocument.resizeImage(size,size);
}

Photoshop CS6のScript機能を使ってGradient(グラデーション)を描画する。

Photoshopでメタリックなテクスチャの制作方法を知りました。
が、キャンパスサイズによって感じがだいぶ変わってしまうので調度良いサイズを得るために何度か作り直していました。
これが徐々に面倒になりスクリプト化しようと思い立ち、何とか完成に。

テクスチャ製作手順

この本に載ってます。本をみなくても下に記述したスクリプトに記述してます。

ほめられデザイン事典 グラフィック・ワークス Photoshop & Illustrator
東 和毅 マスモリタロウ(MINIMUM)、 大西 真平 長場 雄
翔泳社
売り上げランキング: 44876

スクリプト製作でやったこと

  1. Photoshop上の操作をスクリプトに出力
  2. スクリプトを解析
  3. なんとなく抽象化

Photoshop上の操作をスクリプトに出力

Photoshopに”ScriptingListener.plugin”というプラグインを入れます。(Mac)
プラグイン(adobe)
インストール方法も日本語で提供されてます。

このプラグインを入れている間は、デスクトップ上に”ScriptingListenerJS.log”というログファイルが出力されます。
開くとConsoleが立ち上がり中身を表示してくれます。

ただし、プラグインが入っている間はずっとログを出力しつづけるのでファイルが肥大化します。
気づかない間に容量を圧迫しているなんてことがないように、不要な時はプラグインを削除します。

スクリプトを解析

やりた処理がわからない時はPhotoshop上で操作をし、それをLog上から解析することでスクリプトの記述方法がわかります。

解析には次の公式ドキュメントが助けになりました。
ADOBE PHOTOSHOP CS6 JAVASCRIPT SCRIPTING REFERENCE

ポイントになるのは以下の2点です。

ActionDescriptor

何をするにも登場します。
このオブジェクトに色々な情報を付加し、特定の操作を定義します。
形式はDictionaryで、keyとValueで格納します。

特徴は、情報をKeyと値セットで保持します。
保持の際にデータ型をメソッド名を以て指定します。

function addBlur(angle,pix)
{
var idMtnB = charIDToTypeID( "MtnB" );
    var desc170 = new ActionDescriptor();
    var idAngl = charIDToTypeID( "Angl" );
    desc170.putInteger( idAngl, angle );
    var idDstn = charIDToTypeID( "Dstn" );
    var idPxl = charIDToTypeID( "#Pxl" );
    desc170.putUnitDouble( idDstn, idPxl, pix );
executeAction( idMtnB, desc170, DialogModes.NO );
}

こんな感じで入れ子になって各操作や数値が定義されます。
また、”MtnB”等の4文字KeyはAdobe側で定義されているのですが、先のドキュメントに載ってないものばかり吐出されるので、どうにかして欲しいところです。
ここは気合いで。

ActionList

このオブジェクトはデータが時系列的に平行して存在する場合にまとめて定義します。
グラデーションの色データ等を格納するのに使います。

なんとなく抽象化

ログを解析すると似た様な箇所がけっこう出て来るので抽象化すると、構造の理解が深まって幸せになれます。
次に出来上がったものを示します。

サンプル

以下のスクリプトをUTF8形式のテキストファイルに貼付けて、”xxx.jsx”という名前で保存します。
Photoshop上からFile > Script > Browse…で保存したファイルを指定すると実行されます。


//create metalic texture

//Gradient
var width = activeDocument.width;
var height = activeDocument.height;
var startP = new pos(width,0);
var endP = new pos(0,height);
gradient(startP,endP);

//Noise
addNoise(15.0, true, true);

//Blur
//これくらいが調度良かった。
addBlur(0,width/20);

//Mono
hueAndSaturation(0,-100,0);

//data boj
function pos(arg_x,arg_y){
	this.x = arg_x;
	this.y = arg_y;
}

function hueAndSaturation(hue,saturation,lightness)
{
	//hue / saturation
var idHStr = charIDToTypeID( "HStr" );
    var desc = new ActionDescriptor();
	
    var idpresetKind = stringIDToTypeID( "presetKind" );
    var idpresetKindType = stringIDToTypeID( "presetKindType" );
    var idpresetKindCustom = stringIDToTypeID( "presetKindCustom" );
    desc.putEnumerated( idpresetKind, idpresetKindType, idpresetKindCustom );
	
    var idClrz = charIDToTypeID( "Clrz" );
	//Colorize (色彩の統一)
    desc.putBoolean( idClrz, false );
	
    var idAdjs = charIDToTypeID( "Adjs" );
        var list10 = new ActionList();
            var desc175 = new ActionDescriptor();
            var idH = charIDToTypeID( "H   " );
            desc175.putInteger( idH, hue );
            var idStrt = charIDToTypeID( "Strt" );
            desc175.putInteger( idStrt, saturation );
            var idLght = charIDToTypeID( "Lght" );
            desc175.putInteger( idLght, lightness );
        var idHsttwo = charIDToTypeID( "Hst2" );
        list10.putObject( idHsttwo, desc175 );
    desc.putList( idAdjs, list10 );
executeAction( idHStr, desc, DialogModes.NO );	
}
function addBlur(angle,pix)
{
var idMtnB = charIDToTypeID( "MtnB" );
    var desc170 = new ActionDescriptor();
    var idAngl = charIDToTypeID( "Angl" );
    desc170.putInteger( idAngl, angle );
    var idDstn = charIDToTypeID( "Dstn" );
    var idPxl = charIDToTypeID( "#Pxl" );
    desc170.putUnitDouble( idDstn, idPxl, pix );
executeAction( idMtnB, desc170, DialogModes.NO );
}

function addNoise( p,isGsn,isMono )
{
	var idAdNs = charIDToTypeID( "AdNs" );
		var noisDesc = new ActionDescriptor();
		var gaussId;
		if(isGsn)
		{
			gaussId = charIDToTypeID( "Gsn " );
		}else{
			gaussId = charIDToTypeID( "Unfr" );
		}
		var idDstr = charIDToTypeID( "Dstr" );
		noisDesc.putEnumerated( idDstr, idDstr, gaussId );

		var idNose = charIDToTypeID( "Nose" );
		var idPrc = charIDToTypeID( "#Prc" );

		//15% 
		noisDesc.putUnitDouble( idNose, idPrc, p );

		var idMnch = charIDToTypeID( "Mnch" );
		noisDesc.putBoolean( idMnch, isMono );

		var idFlRs = charIDToTypeID( "FlRs" );
		noisDesc.putInteger( idFlRs, 5450347 );
	executeAction( idAdNs, noisDesc, DialogModes.NO );	
	
}

function gradient(stratP,endP)
{
	 //各種情報入れとく場所
	var master = new ActionDescriptor();
	 putStartPosition (master, startP.x, startP.y);
	 putEndPosition (master, endP.x, endP.y);
	 //終点
	 //Enumerated
	var idType = charIDToTypeID( "Type" );
	var idGrdT = charIDToTypeID( "GrdT" );
	var idLnr = charIDToTypeID( "Lnr " );
	master.putEnumerated( idType, idGrdT, idLnr );

	 //Dthr
	var idDthr = charIDToTypeID( "Dthr" );
	master.putBoolean( idDthr, true );

	var idUsMs = charIDToTypeID( "UsMs" );
	master.putBoolean( idUsMs, true );

	var idGrad = charIDToTypeID( "Grad" );
	
	var idNm = charIDToTypeID( "Nm  " );
	var desc12 = new ActionDescriptor();
	  //Asset gradient
	desc12.putString( idNm, """$$$/DefaultGradient/Copper=Copper""" );
	
	var idGrdF = charIDToTypeID( "GrdF" );
	var idCstS = charIDToTypeID( "CstS" );
	desc12.putEnumerated( idGrdF, idGrdF, idCstS );
	
	var idIntr = charIDToTypeID( "Intr" );
	desc12.putDouble( idIntr, 4096.000000 );

	var idClrs = charIDToTypeID( "Clrs" );
	  //最初の色データオブジェクト

		var list2 = new ActionList();
			//RGBColorオブジェクト
			var colorObj1 = createRGBColorObj (151.000006, 70.000003, 26.000000);
			var desc13 = createRGBColorData(colorObj1,0,50);
			var idClrt = charIDToTypeID( "Clrt" );
		list2.putObject( idClrt, desc13 );

		//HSBColorオブジェクト
			/**/
			var colorObj2 = createHSBColorObj(21.115723,21.568627,98.431373);
			var desc15 = createHSBColorData(colorObj2, 1229, 50);
		list2.putObject( idClrt, desc15 );

		//色オブジェクト
			var colorObj3 = createRGBColorObj(108,46,22);
			var desc17 = createRGBColorData(colorObj3,3400,50);
		list2.putObject( idClrt, desc17 );
		   
		//色オブジェクト
			var colorObj4 = createRGBColorObj(239,219,205);
			var desc19 = createRGBColorData(colorObj4,4096,50);
		list2.putObject( idClrt, desc19 );
	desc12.putList( idClrs, list2 );
	
	//終了用リスト
		var list3 = new ActionList();
			var desc21 = new ActionDescriptor();
			putOpt (desc21, 100);
			putLctn (desc21, 0);
			putMdpn (desc21, 50)

			var idTrnS = charIDToTypeID( "TrnS" );
		list3.putObject( idTrnS, desc21 );

			var desc22 = new ActionDescriptor();
			putOpt(desc22,100.0);
			putLctn (desc22, 4096);
			putMdpn (desc22, 50);

			var idTrnS = charIDToTypeID( "TrnS" );
		list3.putObject( idTrnS, desc22 );
	
		var idTrns = charIDToTypeID( "Trns" );
	desc12.putList( idTrns, list3 );

	var idGrdn = charIDToTypeID( "Grdn" );
	master.putObject( idGrad, idGrdn, desc12 );
	
	//実行
	executeAction( idGrdn, master, DialogModes.NO );
}

function putOpt(targetObj,Double)
{
	var idOpct = charIDToTypeID( "Opct" );
	var idPrc = charIDToTypeID( "#Prc" );
	targetObj.putUnitDouble( idOpct ,idPrc ,Double );

}
function putLctn(targetObj,Integer)
{
	var idLctn = charIDToTypeID( "Lctn" );
	targetObj.putInteger( idLctn, Integer );
}
function putMdpn(targetObj,Integer)
{
	var idMdpn = charIDToTypeID( "Mdpn" );
	targetObj.putInteger( idMdpn, Integer  );
}

function createPositionDescriptor(x,y)
{
	var desc	= new ActionDescriptor();
	var idHrzn = charIDToTypeID( "Hrzn" );
	var idRlt = charIDToTypeID( "#Rlt" );
	desc.putUnitDouble( idHrzn, idRlt, x );//key, unitID, value
	var idVrtc = charIDToTypeID( "Vrtc" );
	var idRlt = charIDToTypeID( "#Rlt" );
	desc.putUnitDouble( idVrtc, idRlt, y);
	return desc;
}

function putStartPosition(obj,x,y)
{
	var pos = createPositionDescriptor(x,y);
	//開始位置のKey
	var idFrom = charIDToTypeID( "From" );
	//開始位置のunitID
	var idPnt = charIDToTypeID( "Pnt " );
	//開始位置オブジェクトをグラデーション実行オブジェクトに代入
obj.putObject( idFrom, idPnt, pos );	
}
function putEndPosition(obj,x,y)
{
	var pos = createPositionDescriptor(x,y);
	//開始位置のKey
	var idFrom = charIDToTypeID( "T   " );
	//開始位置のunitID
	var idPnt = charIDToTypeID( "Pnt " );
	//開始位置オブジェクトをグラデーション実行オブジェクトに代入
obj.putObject( idFrom, idPnt, pos );	
}

function createDescriptorWithGradientString(gradient)
{
	if(gradient == null)
	{
		//文字列型のデータを追加。"で囲ったプリセットのグラデーション
		gradient = """$$$/DefaultGradient/BlackWhite=Black, White""";
		gradient = """$$$/DefaultGradient/Copper=Copper""";
	}
	var desc= new ActionDescriptor();
	var idNm = charIDToTypeID( "Nm  " );
	desc.putString( idNm, gradient );
	return desc;
}


function createCMYKColorObj(c,m,y,k)
{
	var desc = new ActionDescriptor();
		var idCyn = charIDToTypeID( "Cyn " );
	desc.putDouble( idCyn, c);
	
		var idMgnt = charIDToTypeID( "Mgnt" );
	desc.putDouble( idMgnt, m );
	
		var idYlw = charIDToTypeID( "Ylw " );
	desc.putDouble( idYlw, y);
	
		var idBlck = charIDToTypeID( "Blck" );
	desc.putDouble( idBlck, k);
	return desc;
}

function createCMYKColorData( colorObj, Lctn)
{
	var  targetObj = new ActionDescriptor();
	var idClr = charIDToTypeID( "Clr " );
	var idCMYC = charIDToTypeID( "CMYC" );
	targetObj.putObject( idClr, idCMYC, colorObj );
	   
		var idType = charIDToTypeID( "Type" );
		var idClry = charIDToTypeID( "Clry" );
		var idUsrS = charIDToTypeID( "UsrS" );
	targetObj.putEnumerated( idType, idClry, idUsrS );
	   
		var idLctn = charIDToTypeID( "Lctn" );
	targetObj.putInteger( idLctn, Lctn );
	   
		var idMdpn = charIDToTypeID( "Mdpn" );
	targetObj.putInteger( idMdpn, 50 );
	return targetObj;
}
function createRGBColorObj(r,g,b)
{
	var desc = new ActionDescriptor();
	var idRd = charIDToTypeID( "Rd  " );
	desc.putDouble( idRd, r );
	var idGrn = charIDToTypeID( "Grn " );
	desc.putDouble( idGrn, g);
	var idBl = charIDToTypeID( "Bl  " );
	desc.putDouble( idBl, b);
	return desc;
}
function createRGBColorData(colorObj,Lctn,Mdpn)
{
	var desc = new ActionDescriptor();
	var idClr = charIDToTypeID( "Clr " );
	var idRGBC = charIDToTypeID( "RGBC" );
	desc.putObject( idClr, idRGBC, colorObj );
	var idType = charIDToTypeID( "Type" );
	var idClry = charIDToTypeID( "Clry" );
	var idUsrS = charIDToTypeID( "UsrS" );
	desc.putEnumerated( idType, idClry, idUsrS );
	putLctn (desc, Lctn);
	putMdpn (desc, Mdpn);
	return desc;
}


function createHSBColorObj(h,s,b)
{
	var desc = new ActionDescriptor();
	
	var idH = charIDToTypeID( "H   " );
	var idAng = charIDToTypeID( "#Ang" );
	desc.putUnitDouble( idH, idAng, h);

	var idStrt = charIDToTypeID( "Strt" );
	desc.putDouble( idStrt, s );

	var idBrgh = charIDToTypeID( "Brgh" );
	desc.putDouble( idBrgh, b);
	
	return desc;
}

function createHSBColorData(HSBObj,Lctn,Mdpn)
{
	var desc = new ActionDescriptor();
	
	var idClr = charIDToTypeID( "Clr " );
	var idHSBC = charIDToTypeID( "HSBC" );
	desc.putObject( idClr, idHSBC, HSBObj );

	var idType = charIDToTypeID( "Type" );
	var idClry = charIDToTypeID( "Clry" );
	var idUsrS = charIDToTypeID( "UsrS" );
	desc.putEnumerated( idType, idClry, idUsrS );
	
	putLctn(desc,Lctn);
	putMdpn (desc, Mdpn);
	return desc;
}

function blackGradient()
{
	
	//グラデーションを実行するオブジェクトを生成
var master = new ActionDescriptor();
	
	putStartPosition(master,0.0,0.0);
	putEndPosition (master, 600, 0.0);

		var idType = charIDToTypeID( "Type" );
		var idGrdT = charIDToTypeID( "GrdT" );
		var idLnr = charIDToTypeID( "Lnr " );
master.putEnumerated( idType, idGrdT, idLnr );
	
		var idDthr = charIDToTypeID( "Dthr" );
master.putBoolean( idDthr, true );
	
		var idUsMs = charIDToTypeID( "UsMs" );
master.putBoolean( idUsMs, true );//bool 型のデータを追加
	
		//ここからGradient Data
		var gradientData =  createDescriptorWithGradientString(); //new ActionDescriptor();
		
			var idIntr = charIDToTypeID( "Intr" );
gradientData.putDouble( idIntr, 4096.000000 );
//gradientData.putDouble( idIntr, 0.000000 );
		
		//
		var colorList = new ActionList();
			//black color obj
			var black = createCMYKColorObj(75.02,68.01,67,90.19);	
			var colorData1 =  createCMYKColorData(black,0);

			var idClrt = charIDToTypeID( "Clrt" );
			colorList.putObject( idClrt, colorData1 );

			var white = createCMYKColorObj(0,0,0,0);
			var colorData2 = createCMYKColorData(white,4096);
			colorList.putObject( idClrt, colorData2 );

		var idClrs = charIDToTypeID( "Clrs" );
		gradientData.putList( idClrs, colorList );

		var list5 = new ActionList();

			var desc34 = new ActionDescriptor();
			putOpt(desc34,100.0);
			putLctn(desc34,0);
			putMdpn(desc34,50);

			var idTrnS = charIDToTypeID( "TrnS" );
		list5.putObject( idTrnS, desc34 );
		
			var desc35 = new ActionDescriptor();
			putOpt(desc35,100.0);
			putLctn (desc35, 4096);
			putMdpn (desc35, 50);
				
			var idTrnS = charIDToTypeID( "TrnS" );
		list5.putObject( idTrnS, desc35 );

			var idTrns = charIDToTypeID( "Trns" );
		gradientData.putList( idTrns, list5 );

	var idGrad = charIDToTypeID( "Grad" );//key
	var idGrdn = charIDToTypeID( "Grdn" );//unitID
	master.putObject( idGrad, idGrdn, gradientData );
executeAction( idGrdn, master, DialogModes.NO );
}

応用

色オブジェクト(次のサンプル部分)をgradient()に追加して複雑なグラデーションを作成してみると面白いかもしれません。

//色オブジェクト
	var colorObj3 = createRGBColorObj(108,46,22);//createRGBColorObj(r,g,b);
	var desc17 = createRGBColorData(colorObj3,3400,50);
	list2.putObject( idClrt, desc17 );

JavaScript 第6版

【Unity】簡易AnimationViewer

簡易アニメーションビューワを作成してみたのでサンプルコードとその解説を残す。

MotionVIewer Screen Shot 0024-03-26 at 17.20.10

やること

  • 新しいシーンを作成
  • モーション付きのモデルをインスタンシング
  • CharacterControllerコンポーネントを追加
  • スクリプト追加

新しいシーンを作成

Command + Shift + N で新しいシーンを作成する。

シーンビューにはカメラしか写ってない。

MotionVIewer Screen Shot 0024-03-26 at 15.29.16

モーション付きのモデルをインスタンシング

アセットストア等からデータを入手できるので入手しておく。

これをプロジェクトビューからシーンビューかHierarchyにD&Dでインスタンシングする。

MotionVIewer Screen Shot 0024-03-26 at 17.33.01

インスタンシングされたオブジェクトを選択し、Inspectorに表示されたTransformの座標を全てゼロにしておく。

この時点でモノによってはカメラに納まっている。

CharacterControllerコンポーネントを追加

Component->Physics->Character Controllerを選択し、先にインスタンシングしたオブジェクトにコンポーネントを追加する。

Inspector の AnimationsにあるElementを見てアニメーションがちゃんと存在するを確認しておく。

MotionVIewer Screen Shot 0024-03-26 at 15.42.29

スクリプトの追加

下記のスクリプトをコピペして追加する。

#pragma strict
var roteSpeed:float = 3.5;

private var side : float = 1.0;
private var animationSpeed:float;
private var animationCount:uint;
private var animationList:Array;
function Start () {
     print("animationGetCount:" + animation.GetClipCount());
     print(animation.clip.name);
     animationCount = animation.GetClipCount();
     print(gameObject.animation);
     animationList = GetAnimationList();
}

function Update () {
     if( -0.9 < transform.rotation.y && transform.rotation.y < 0.9 ){
     }else{
          side *= -1;
     }
     transform.rotation.y += roteSpeed/180 * side;

}

function OnGUI (){
     var sw : int = Screen.width;
     var sh : int = Screen.height;
     var margin : int = 10;
     //ghool position
     var ghoolRoteYLabel:Rect = Rect(0,margin + 0,sw,sh/10);
     GUI.Label(ghoolRoteYLabel,"Rote Y:"+ transform.rotation.y);

     margin += 10;

     //slider
     var playSpeedRect:Rect = Rect(0,margin + 30,sw,sh/10);
     animationSpeed = GUI.HorizontalSlider(playSpeedRect,animationSpeed,0.0,5.0);
     for (var state : AnimationState in animation)
     {
          state.speed = animationSpeed;
     }
     GUI.Label(playSpeedRect,"playSpeed :"+ animationSpeed);

     margin += 100;

     //Buttons
     var buttonSpace:int = 40;
     var rectWidth:int = 100;
     var rectHeight:int = 40;
     var max:int = 10;
     var rects:Array = new Array();
     var i:int = 0;

     for (var name : String in animationList)
     {
          var rect:Rect = Rect(15,margin + 20*i + buttonSpace*i, rectWidth,rectHeight);
          if(GUI.Button(rect,animationList[i].ToString())){
               animation.CrossFade(animationList[i],0.01);
          }
          i++;
     }
}

private function GetAnimationList():Array
{
     var tmpArray = new Array();
     for (var state : AnimationState in gameObject.animation)
     {
          tmpArray.Add(state.name);
     }
     return tmpArray;
}

これを先のインスタンスしたオブジェクトにD&Dするとコンポーネントとして追加される。
再生ボタンで実行を行うと保持しているモーション一覧がボタン化されて列記される。
それらをクリックするとモーションが変更される。
ループ指定がOffだとモーションは一度再生された後に最終フレームで停止する。
スライダは再生速度を変動させる。ループ再生しているモーションを確認する際は便利。

MotionVIewer Screen Shot 0024-03-26 at 17.54.11

スクリプトの解説

Startメソッドで各種初期化。

var roteSpeed:float = 3.5;

private var side : float = 1.0;
private var animationSpeed:float;
private var animationCount:uint;
private var animationList:Array;
function Start () {
     print("animationGetCount:" + animation.GetClipCount());
     print(animation.clip.name);
     animationCount = animation.GetClipCount();
     print(gameObject.animation);
     animationList = GetAnimationList();
}

自身の保持するClip(アニメーション1個の単位)の数を取得。
同時に自作の関数でClipの名前を配列で取得。

UpdateメソッドではキャラのY軸上の回転を行わせている。

function Update () {
     if( -0.9 < transform.rotation.y && transform.rotation.y < 0.9 ){
     }else{
          side *= -1;
     }
     transform.rotation.y += roteSpeed/180 * side;
}

OnGUIメソッドではUI表示。

function OnGUI (){
     var sw : int = Screen.width;
     var sh : int = Screen.height;
     var margin : int = 10;
     //ghool position
     var ghoolRoteYLabel:Rect = Rect(0,margin + 0,sw,sh/10);
     GUI.Label(ghoolRoteYLabel,"Rote Y:"+ transform.rotation.y);

     margin += 10;

     //slider
     var playSpeedRect:Rect = Rect(0,margin + 30,sw,sh/10);
     animationSpeed = GUI.HorizontalSlider(playSpeedRect,animationSpeed,0.0,5.0);
     for (var state : AnimationState in animation)
     {
          state.speed = animationSpeed;
     }
     GUI.Label(playSpeedRect,"playSpeed :"+ animationSpeed);

     margin += 100;

     //Buttons
     var buttonSpace:int = 40;
     var rectWidth:int = 100;
     var rectHeight:int = 40;
     var max:int = 10;
     var rects:Array = new Array();
     var i:int = 0;

     for (var name : String in animationList)
     {
          var rect:Rect = Rect(15,margin + 20*i + buttonSpace*i, rectWidth,rectHeight);
          if(GUI.Button(rect,animationList[i].ToString())){
               animation.CrossFade(animationList[i],0.01);
          }
          i++;
     }
}

スライダはアニメーションの再生速度の変更を担う。
変更時に保持するアニメーション全ての再生速度を同時に変更する。
その後、自動でアニメーション数に応じてボタンを生成。
ボタン押下時の効果はクロスフェードさせてアニメーションをスイッチする、という内容。
アニメーションの名前が分からなくてもElement#で拾えてたと思ってサンプル探したけど出て来ず。
仕方なくAnswersで検索したら発見。コピペ。

GetAnimationListメソッド

private function GetAnimationList():Array
{
     var tmpArray = new Array();
     for (var state : AnimationState in gameObject.animation)
     {
          tmpArray.Add(state.name);
     }
     return tmpArray;
}

for inで、gameObjectが保持するAnimationState形式のデータを全て取得する。
取得したAnimationState各種のClipのnameプロパティ(任意で指定可能な文字列)を取得して配列に格納している。
ループが完了したら配列は返される。

参考

[Unity]MonoBehaviour

http://unity3d.com/support/documentation/ScriptReference/Behaviour.html

MonoBehaviour Inherits from Behaviour MonoBehaviour is the base class every script derives from.

Using Javascript every script automatically derives from MonoBehaviour. When using C# or Boo you have to explicitly derive from MonoBehaviour.

Note: The checkbox for disabling a MonoBehavior (on the editor) will only prevent Start(), Awake(), Update(), FixedUpdate(), and OnGUI() from executing. If none of these functions are present, the checkbox is not displayed.

See Also: The chapter on scripting in the manual.

訳:

MonoBehaviour Behaviourから継承。

MonoBehaviour は全てのスクリプトが導き出される基底クラス。

Javascriptを使用した際のスクリプトは自動的にMonoBehaviourから導き出されます。

C#やBooを使用する時はMonoBehaviourから導き出す事を明示しなければなりません。

Note:

MonoBehaviorの機能を切るためのエディター上の

チェックボックスは、次の処理を無効化。

Start(), Awake(), Update() FixedUpdate() OnGUI()

これらの関数が用意されてなければチェックボックスが表示されません。

マニュアルの chapter on scripting を参照。

UnityでUI上にボタンを配置してボタンからメソッドを実行させる最小構成

UI上にボタンを表示してタッチ/クリックできるようにします。

作業手順

  1. GUIスキンを用意
  2. OnGUIイベントにボタン表示と処理を書いたスクリプトを用意
  3. 実行

GUIスキン

GUIをProject上を右クリックしてGUI Skinを選択する。

Unity_Button_WhereIsGUIAsset

黒いアイコンのAssetが出現。

これを選択してInspector上で各種定義を行う。

今回はボタンをメインで扱うのでボタンの要素を触ってみる。
Unity_Button_UISkinInspector

“Hover”はマウスオーバー時の変化であり、iOSではタッチのみなので表現されない。
実際には”Active”のみ。

スクリプト

var skin:GUISkin;//グローバル変数
function OnGUI(){
GUI.skin = skin;

var sw:int = Screen.width;
var sh:int = Screen.height;
var buttonRect:Rect = Rect(0, sh*7/8, 60,30);
if(GUI.Button(buttonRect,"left"))
{
    Destroy(gameObject.FindWithTag("Player"));
}

スクリプトを書き終えたらGUIを割り当てる。スクリプ上のグローバル変数化しているSkinに対して、先に用意したGUISkinをドラッグ&ドロップで割り当てる。

Unity_Button_SetUISkinToScript

これでGUISkinで定義したボタンの色などが適用される。やらないとデフォルトの定義が割り当てられるので不要ならば”割り当てない”選択もアリ。

そして Shift + Command + N でNullオブジェクトを生成し、そのコンポーネントとしてスクリプトをドラッグ&ドロップで割り当てると完成。

ちなみにだけど、ほんとは

gameObject.FindWithTag("Player").SendMessage("Kill")

とかでメッセージ送信だけにしておくべき。が、最小構成ではなくなるので今回は凄く単純なスクリプトにしておきました。

↓結果はかの通り。文字色が赤いのはマウスオーバーのタイミングでキャプチャしたため。

Unity_Button_result

参考

http://www.uxic.net/archives/tag/unity

Unity入門 ~高機能ゲームエンジンによるマルチプラットフォーム開発~

雑記

本件とちょっと主旨ズレるけどサンプルコード載せてるサイトでちょくちょく入ってる

var sw:int = Screen.width;

というコードがあると「あ、Screenってクラスから何か取得できるんだ、へー」って事があって助かる。

前に一度、サンプルコードはどんどん載せるべき、というはてなのエントリがあったけどその通りだと思う。

Unityの衝突処理最小構成

Unity教本の内容を整理。

最初は、gameObjectがどこからやってきたのかが分からなかったので理解するためにノートをまとめていた。そしたら変な方向に…。

原文

function OnCollisionEnter (collisionInfo : Collision) {
     if (collisionInfo.gameOnbect.tag == "Box"){
          Destroy(collisionInfo.gameObject);
     }
     Destroy(gameObject);
}

日本語訳

我に触れるものあれば即座に全うす。
  もし、当たる者の持つ遊戯物体が内に秘めたる”Box”と一致するならば全うす。
    死の呪文を触れた遊戯物体に喰らわせようぞ。
  さもなくばおわる
  死の呪文をば自ら喰らおうぞ。
これにて全うした。

死の呪文(Destroy()):オブジェクトをシーン上から破棄する効果がある。

おわり

良いよねこういう方向でも。

Unity入門 ~高機能ゲームエンジンによるマルチプラットフォーム開発~

GoogleAppを使ってリマインダーを作ってみた。

作った動機は以下の記事を読んで。
http://readingmonkey.blog45.fc2.com/blog-entry-556.html

効率良く記憶/勉強するためには特定の法則間隔でリマインドするのが効率良いらしい。
でもそのリマインドの間隔ではOmnifocusではやれない。

うまいことやれないかなーと考えてみたらGoogleカレンダー上に通知イベントを生成するスクリプトを書けばいいじゃんと思い至る。

んでちょいと調べた結果、可能だということが分かったので早速作成。
作業2時間。久々にJavascript触った割りにちゃんとできました。

要件整理

  • ボタンを押すと前述の前述のURLの間隔単位でGoogleCalendarにイベントを生成してくれる。
  • イベント名は任意で。
  • リマインドしてくれる回数もしていさせて欲しい。
  • 可能であれば今何回目なのかも知らせて欲しい。
  • リマインダーなんだから通知して欲しい。
  • 詳細もメモできたらいいな。

とする。

作り方

  1. google docsに新しいスプレッドシートを作成する
  2. スクリプトエディタを開いて後述のソースをコピペする
  3. ボタンを設置してfunctionを関連づけさせとく
  4. google calendarに新しいカレンダーを作り、ソース上に記述されてるカレンダー名と同じ名前を付けておく
  5. 4のカレンダーの通知の項目にメールとかポップアップとかでリマインドさせるようにしとく
  6. 完成!

1:google docsに新しいスプレッドシートを作成する

割愛

2:スクリプトエディタを開いて後述のソースをコピペする

スプレッドシートを開き、メニューバーからツール->スクリプトエディタでエディタを開き、
後述のソースをコピペします。

3:ボタンを設置してfunctionを関連づけさせとく

makeRemindEventOnCal change inspector
メニューバーから↑の手順で図形描画を選択し、”図形描画を挿入”画面に移行し、
makeRemindEventOnCal create rect
矩形を配置します。
makeRemindEventOnCal draw rect
矩形が用意できたら右上の”保存して閉じる”ボタンを押してスプレッドシートに戻ります。
makeRemindEventOnCal Add Script
スプレッドシートに描画された図形にマウスオーバーすると”図形描画”の文字が。ここのプルダウンからスクリプトを関連づけさせます。
makeRemindEventOnCal set script
記述する関数名はソースの2つ目の関数名を記述します。
“makeRemindEventOnCal change inspector”

4:google calendarに新しいカレンダーを作り、ソース上に記述されてるカレンダー名と同じ名前を付けておく

自分のアカウントのグーグルカレンダーを開いて、新しいカレンダーを追加します。カレンダーに名前を付けれるのでソースで指定してある”記憶強化リマインダ”に。

名前を変えるのであればソース側のカレンダー名も偏向すること。

5:4のカレンダーの通知の項目にメールとかポップアップとかでリマインドさせるようにしとく

makeRemindEventOnCal alert setting
カレンダーの通知の項目を開いて、メールやポップアップに適当な時間を記述して通知してくれるようにしときます。これを設定しないとただのカレンダー止まりなのでちゃんと能動的に動いてくれる様にします。

6:完成!

ここまでやれば完成。動作は以下の通りです。
ReminderOnCalendar write eventName
イベント名を記述。
カレンダー上に表記される文字なので短く分かりやすいものに。
ReminderOnCalendar write loop count
最初の1ヶ月は間隔が長くなる不定期なので、固定でリマインドするようにしてあります。
その後の安定的(31日毎)にリマインドする回数を指定します。
ReminderOnCalendar write description
カレンダーに記載する詳細を入力する欄。思い出すべき内容を出来るだけ細かく書くか、何をやるかだけ書くか、見出し以外の事を書いときます。
ReminderOnCalendar run script end
スクリプトが完了するとこれが表示されます。

ではカレンダーを見てみます…。

makeRemindEventOnCal result1
おお!ちゃんと書かれてる!
makeRemindEventOnCal result2
詳細もご覧の通り記述されます。

ソース

ソースは以下の通り。

/*
カレンダーにリマインダーの理論間隔でイベントを設置するスクリプト
ver. 0.1 2012/1/3: OHGAKI Kunihiro
ver. 0.2 2012/1/4: OHGAKI Kunihiro  : コメントを書き直し。
*/

/*
makeCalEvent
カレンダーにイベントを書き込む関数
cal as Calendar Object
カレンダーオブジェクト。何度も取得するのもアレなので一度取得したら保持させるようにしたので引数で渡してます。
グローバル変数にしとけよって話だ…。
GoogleAppってグローバル変数ってつかえるのかね

eventName as String.
カレンダーに記述するイベント名を文字列で取得。

targetDate as Date
実際に記述する日時を日付で指定。

count as Integer
リマインドした回数を詳細とかイベント名に記述する。

descript as String
カレンダーのイベントの詳細部分に記述する文字列。

*/

function makeCalEvent(cal,eventName, targetDate,count,descript){
  var cname = eventName;
  if (cname != "" & cname != "cancel"){
    targetDate.setHours(0);
    targetDate.setMinutes(0);
    targetDate.setSeconds(0);
//    Browser.msgBox(descript);
    cal.createEvent(
      eventName+"@"+count+"回目",
      targetDate,
      targetDate,
      {
        description:count + "回目のリマインド" + descript,
        sendInvites:false
      }
    );
  }
}

/*
メインの関数な。スプレッドシートから呼び出すのはこれ。
makeRemindEventOnCal カレンダーにリマインダーイベントをセットするスクリプト。
リマインドの間隔は以下のURLの記事を元に作成してます。
http://readingmonkey.blog45.fc2.com/blog-entry-556.html
*/
function makeRemindEventOnCal(){
    var cal = CalendarApp.openByName("記憶強化リマインダ"); //←リマインダー専用のカレンダーを予め作成。名前は任意で。

  //カレンダーに表示するイベント名を入力する。
  var eventName = Browser.inputBox("イベント名を入力してください");
  if(!eventName)return;
  //リマインドする回数を入力。
  var count = Browser.inputBox("安定期後にリマインドしたい回数を数値で入力", "整数でよろしく", null);
  var description = Browser.inputBox("詳細を記述してください。未記入でもOK");
  var remindArray = [1,2,4,7,15,21];
  //軌道に乗るまでの1ヶ月はこの間隔で広げて行く。
  var remindMonth = 31;
  //軌道にのったら一月以上あけてはならないらしい。
  var beforStationaryPhaseCount = remindArray.length;
  for(var i = 0; i < beforStationaryPhaseCount; i++){
    var day = new Date;
    var target = remindArray.shift();
    day.setTime(day.getTime() + target*24*3600*1000);//指定の日にリマインドタイミングをセット。
    makeCalEvent(cal,eventName,day,i+1,description);
  }
  var afterDay = new Date;
  if(count != 0){
    for(var i = 0; i < count; i++ ){
      afterDay.setTime(afterDay.getTime() + remindMonth*24*3600*1000);//31日後にセット。
      makeCalEvent(cal,eventName,afterDay,i + beforStationaryPhaseCount +1,description);
    }
  }
      //完了通知
  Browser.msgBox("イベントのセットが完了しました。");
}

ハマったところ

カレンダーのイベントを生成する.createEvent()の引数に詳細を文字列で入力するんだけども、この引数がAdvanced Argumentsであり、省略可能引数オブジェクトのプロパティで、最初に発見したサンプルはバージョン違いなのか記述方法が違っていた。エラーが出ていたので本家サイトを探すに至り、難を逃れた。今後は最初から本家を見るように心がける。日本語サイトに逃げがちなのを戒めますわよ。

あと、タイポで反映されてなかったのに気づかずに5分くらい悩んだ。相変わらずエラー出ないのでこういうバグ発見はマジ困る。ブラウザ上のエディタではなくてローカルのちゃんとしたエディタで作業すべきなんだろうな。※英語記述のタイポを戒めてくれるエディタを教えて下さい!英語力低い僕には(ry

参考にしたサイト