Ruby で Amazon Product Advertising API を使ったメモ
はじめに
- Ruby で amazon-ecs を使って Amazon Product Advertising API の検索をしたメモ。
インストール
gem 'amazon-ecs'
アカウント作成
Amazon アソシエイトのID作成
商品リストの検索
初期化
require 'amazon/ecs' Amazon::Ecs.options = { :associate_tag => 'Amazon アソシエイトのID', :AWS_access_key_id => 'AWS のアクセスキー', :AWS_secret_key => 'AWS のシークレットキー' }
キーワードで検索
# 検索の実行 / オプションの詳細は後述 amazon = Amazon::Ecs.item_search( 'キーワード', :search_index => 'Books', #=> 検索対象の指定 / 詳細は後述 :response_group=>"Large", #=> レスポンスに含まれる要素の指定 / 詳細は後述 :country => 'jp' ) # 各商品ごとに処理 amazon.items.each do |item| puts item.class #=> 商品ごとに Amazon::Element のインスタンスが渡される puts item.get('ItemAttributes/Title') #=> タイトルの取得(Amazon::Element.get(PATH) でパスを指定して値の取得) puts item.get('DetailPageURL') #=> 商品詳細URL puts item.get("LargeImage/URL") #=> 商品画像URL puts item.get_element('ItemAttributes').get("Title") #=> Amazon::Element.get_element(ELEMENT_NAME) で子要素を取得 puts item.get_element('ItemAttributes').elem.css("Title").text #=> Amazon::Element.elem で Nokogiri::XML::Element が取得できるので、css や xpath で要素の検索ができる end
検索オプション
- https://images-na.ssl-images-amazon.com/images/G/09/associates/paapi/dg/index.html を参考に
- パラメータ名を snake_case にする
search_index
- 検索対象の指定
- All で全て、Books で本、KindleStore でKindle本、その他は https://images-na.ssl-images-amazon.com/images/G/09/associates/paapi/dg/index.html を参照。
response_group
- レスポンスに含まれる要素の指定
- キーワードで指定し、復数の場合はカンマ区切りにする
- search_index によって指定可能な値が異なる
- 指定可能な値は https://images-na.ssl-images-amazon.com/images/G/09/associates/paapi/dg/index.html を参照
- Small, Medium, Large はそれぞれのサイズ感でよく使うものをまとめてある
browse_node
- 検索カテゴリーの指定。カテゴリーのIDを整数で指定する。
- IDの表などは見つけられなかったが、www.amazon.co.jp のカテゴリー別商品ページなどの URL から推測できる。
- 商品ページのURL が http://www.amazon.co.jp/%E6%9C%AC-%E9%80%9A%E8%B2%A9/b/ref=nav_jb?ie=UTF8&node=465392 の場合は 465392
item_page
- 商品は1リクエストで10件取得され、11件目以降は item_page でページ数を指定して取得する。
- 指定できる値は1〜10
実行例
Kindleストアでコミックを検索し売り上げ順にソートした2ページ目を取得、カテゴリーのIDを表示する。
amazon = Amazon::Ecs.item_search( '', #=> browse_node 指定時にはキーワードは省略可 :search_index => 'KindleStore', :response_group=>"Small, BrowseNodes", :country => 'jp', :browse_node => '2293143051', :sort => "salesrank", :item_page => "2" ) amazon.items.each do |item| puts "Title: " + item.get('ItemAttributes/Title') puts item.get("BrowseNodes/BrowseNode/BrowseNodeId") #=> メインのカテゴリーID if item.get_element("BrowseNodes/BrowseNode/Children") then #=> サブのIDがあれば表示 item.get_element("BrowseNodes/BrowseNode/Children").elem.children.each {|c| puts c.css("BrowseNodeId").text } end end
Unity でユーザーデータをJSONにシリアライズして暗号化して保存する
はじめに
- Unity でユーザーのデータを保存したい場合、最も手軽なのは PlayerPrefs だが、List が使えなかったり平文だったりして不便な面もある。
- 今回はユーザーデータを LitJSON でシリアライズして、永続データ領域に保存。保存時に暗号化、読み込み時に復号化、といったことを試してみた。
- 基本的に 【制作実習】Unityでユーザーデータの保存 | ゴゴゴゴ にある通りだが、省略されている部分を補完する。
LitJSON を使って設定データを読み書きする
LitJSON の導入
- http://lbv.github.io/litjson/ から dll をダウンロードして Assets 以下の任意のパスに追加
ラッパークラスの作成
- LitJSON だと float 型が扱えないとか有るそうなのでラッパーの作成
using LitJson; // via http://caitsithware.com/wordpress/archives/140 public class JsonMapper { static JsonMapper() { LitJson.JsonMapper.RegisterExporter<float>( (obj, writer) => { writer.Write( System.Convert.ToDouble( obj ) ); } ); LitJson.JsonMapper.RegisterImporter<double,float>( (input) => { return System.Convert.ToSingle( input ); } ); LitJson.JsonMapper.RegisterImporter<System.Int32,long>( (input) => { return System.Convert.ToInt64( input ); } ); } public static T ToObject<T>( string json ) { return LitJson.JsonMapper.ToObject<T>( json ); } public static string ToJson( object obj ) { return LitJson.JsonMapper.ToJson ( obj ); } }
設定データのクラスを作成
- 内容は任意で
using System.Collections; using System.Collections.Generic; public class GameData { public int level; public List<int> characterIds; }
設定データ読み書き用クラスの作成
using UnityEngine; using System.IO; public class GameDataManager { static string fileName = "GameData"; // via http://www.gogogogo.jp/issue/diary/%E4%B8%80%E4%BA%BA%E5%A4%A7%E5%AD%A6/%E5%88%B6%E4%BD%9C%E5%AE%9F%E7%BF%92/1909/ public static void Save (GameData gameData) { string json = JsonMapper.ToJson(gameData); string filePath = GetFilePath (); FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write); BinaryWriter writer = new BinaryWriter(fileStream); writer.Write(json); writer.Close(); } // via http://www.atmarkit.co.jp/fdotnet/dotnettips/669bincopy/bincopy.html public static GameData Load () { string filePath = GetFilePath (); FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); BinaryReader reader = new BinaryReader(fileStream); GameData gameData = new GameData (); if (reader != null) { string str = reader.ReadString(); gameData = JsonMapper.ToObject<GameData> (str); reader.Close(); } return gameData; } static string GetFilePath () { return Application.persistentDataPath + "/" + fileName; } }
読み書きのテスト
- 下記のスクリプトを任意の GameObject にアタッチして再生して確認する
- Mac だと ~/Library/Caches/"PlayerSettingsのCompanyName"/"Project名"/GameData に有るファイルからも確認できる
sing UnityEngine; using System.Collections; using System.Collections.Generic; public class GameDataTest : MonoBehaviour { // Use this for initialization void Start () { GameData d = new GameData (); d.level = 10000; d.characterIds = new List<int> { 1, 2, 3 }; GameDataManager.Save (d); GameData d2 = GameDataManager.Load (); Debug.Log (d2.level); } }
設定データの暗号化
- 設定データ読み書き用クラスを編集して、保存されるデータを暗号化する
using UnityEngine; using System.IO; using System.Security.Cryptography; public class GameDataManager { static string fileName = "GameData"; public static void Save (GameData gameData) { string json = JsonMapper.ToJson (gameData); json += "[END]"; // 復号化の際にPaddingされたデータを除去するためのデリミタの追記 string crypted = Crypt.Encrypt (json); string filePath = GetFilePath (); FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write); BinaryWriter writer = new BinaryWriter(fileStream); writer.Write(crypted); writer.Close(); } public static GameData Load () { string filePath = GetFilePath (); FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); BinaryReader reader = new BinaryReader(fileStream); GameData gameData = new GameData (); if (reader != null) { string str = reader.ReadString(); string decrypted = Crypt.Decrypt (str); decrypted = System.Text.RegularExpressions.Regex.Replace (decrypted, @"\[END\].*$", ""); gameData = JsonMapper.ToObject<GameData> (decrypted); reader.Close(); } return gameData; } static string GetFilePath () { return Application.persistentDataPath + "/" + fileName; } // via http://yukimemo.hatenadiary.jp/entry/2014/04/15/023802 private class Crypt { private const string AesIV = @"jCddaOybW3zEh0Kl"; private const string AesKey = @"giVJrbHRlWBDIggF"; public static string Encrypt( string text ) { RijndaelManaged aes = new RijndaelManaged (); aes.BlockSize = 128; aes.KeySize = 128; aes.Padding = PaddingMode.Zeros; aes.Mode = CipherMode.CBC; aes.Key = System.Text.Encoding.UTF8.GetBytes (AesKey); aes.IV = System.Text.Encoding.UTF8.GetBytes (AesIV); ICryptoTransform encrypt = aes.CreateEncryptor (); MemoryStream memoryStream = new MemoryStream (); CryptoStream cryptStream = new CryptoStream (memoryStream, encrypt, CryptoStreamMode.Write); byte[] text_bytes = System.Text.Encoding.UTF8.GetBytes (text); cryptStream.Write (text_bytes, 0, text_bytes.Length); cryptStream.FlushFinalBlock (); byte[] encrypted = memoryStream.ToArray (); return (System.Convert.ToBase64String (encrypted)); } public static string Decrypt( string cryptText ) { RijndaelManaged aes = new RijndaelManaged (); aes.BlockSize = 128; aes.KeySize = 128; aes.Padding = PaddingMode.Zeros; aes.Mode = CipherMode.CBC; aes.Key = System.Text.Encoding.UTF8.GetBytes (AesKey); aes.IV = System.Text.Encoding.UTF8.GetBytes (AesIV); ICryptoTransform decryptor = aes.CreateDecryptor (); byte[] encrypted = System.Convert.FromBase64String (cryptText); byte[] planeText = new byte[encrypted.Length]; MemoryStream memoryStream = new MemoryStream (encrypted); CryptoStream cryptStream = new CryptoStream (memoryStream, decryptor, CryptoStreamMode.Read); cryptStream.Read (planeText, 0, planeText.Length); return (System.Text.Encoding.UTF8.GetString (planeText)); } } }
参考サイト
http://www.gogogogo.jp/issue/diary/%E4%B8%80%E4%BA%BA%E5%A4%A7%E5%AD%A6/%E5%88%B6%E4%BD%9C%E5%AE%9F%E7%BF%92/1909/
http://caitsithware.com/wordpress/archives/140
http://lbv.github.io/litjson/docs/quickstart.html
http://yukimemo.hatenadiary.jp/entry/2014/04/15/023802
http://qiita.com/snaka/items/a99747daadfdb0d9ea0b
http://dobon.net/vb/dotnet/string/trim.html
2D Roguelike を試してみて使いまわせそうなイディオム
はじめに
- Unity のチュートリアル 2D RogueLike が気になったので、試してみて使えそうなイディオムをいくつかメモ。
- ゲーム自体は、矢印で移動、移動する毎にfood減少、アイテムでfood増加、敵の攻撃でfood減少、階段で次の階へ、アイテム・壁・敵は階ごとにランダム、という単純な感じ
シングルトン
- GameManager.cs をシングルトンとして扱う
public static GameManager instance = null; //Static instance of GameManager which allows it to be accessed by any other script. //Awake is always called before any Start functions void Awake() { //Check if instance already exists if (instance == null) //if not, set instance to this instance = this; //If instance already exists and it's not this: else if (instance != this) //Then destroy this. This enforces our singleton pattern, meaning there can only ever be one instance of a GameManager. Destroy(gameObject); //Sets this to not be destroyed when reloading scene DontDestroyOnLoad(gameObject); ... }
敵、壁、アイテムのランダム配置
- 配置する位置の候補を List にしておき...
//Clears our list gridPositions and prepares it to generate a new board. void InitialiseList () { //Clear our list gridPositions. gridPositions.Clear (); //Loop through x axis (columns). for(int x = 1; x < columns-1; x++) { //Within each column, loop through y axis (rows). for(int y = 1; y < rows-1; y++) { //At each index add a new Vector3 to our list with the x and y coordinates of that position. gridPositions.Add (new Vector3(x, y, 0f)); } } }
- 候補からランダムに選出して List から除外する
//RandomPosition returns a random position from our list gridPositions. Vector3 RandomPosition () { //Declare an integer randomIndex, set it's value to a random number between 0 and the count of items in our List gridPositions. int randomIndex = Random.Range (0, gridPositions.Count); //Declare a variable of type Vector3 called randomPosition, set it's value to the entry at randomIndex from our List gridPositions. Vector3 randomPosition = gridPositions[randomIndex]; //Remove the entry at randomIndex from the list so that it can't be re-used. gridPositions.RemoveAt (randomIndex); //Return the randomly selected Vector3 position. return randomPosition; }
指定の位置まで移動する
- Physics2D.Linecast で2点間に引いた線と衝突するものを調べることで移動可能かどうか確認する
- Physics2D.Linecast 実行中は動かしたいオブジェクト自体の Collider を disable にする
//Move returns true if it is able to move and false if not. //Move takes parameters for x direction, y direction and a RaycastHit2D to check collision. protected bool Move (int xDir, int yDir, out RaycastHit2D hit) { //Store start position to move from, based on objects current transform position. Vector2 start = transform.position; // Calculate end position based on the direction parameters passed in when calling Move. Vector2 end = start + new Vector2 (xDir, yDir); //Disable the boxCollider so that linecast doesn't hit this object's own collider. boxCollider.enabled = false; //Cast a line from start point to end point checking collision on blockingLayer. hit = Physics2D.Linecast (start, end, blockingLayer); //Re-enable boxCollider after linecast boxCollider.enabled = true; //Check if anything was hit if(hit.transform == null) { //If nothing was hit, start SmoothMovement co-routine passing in the Vector2 end as destination StartCoroutine (SmoothMovement (end)); //Return true to say that Move was successful return true; } //If something was hit, return false, Move was unsuccesful. return false; }
- 移動できなかった場合に衝突したものを取得する例
//The virtual keyword means AttemptMove can be overridden by inheriting classes using the override keyword. //AttemptMove takes a generic parameter T to specify the type of component we expect our unit to interact with if blocked (Player for Enemies, Wall for Player). protected virtual void AttemptMove <T> (int xDir, int yDir) where T : Component { //Hit will store whatever our linecast hits when Move is called. RaycastHit2D hit; //Set canMove to true if Move was successful, false if failed. bool canMove = Move (xDir, yDir, out hit); //Check if nothing was hit by linecast if(hit.transform == null) //If nothing was hit, return and don't execute further code. return; //Get a component reference to the component of type T attached to the object that was hit T hitComponent = hit.transform.GetComponent <T> (); //If canMove is false and hitComponent is not equal to null, meaning MovingObject is blocked and has hit something it can interact with. if(!canMove && hitComponent != null) //Call the OnCantMove function and pass it hitComponent as a parameter. OnCantMove (hitComponent); }
Position の Tween
- プレイヤーや敵など移動するオブジェクトの親クラス MovingObject.cs でスムーズに位置を変更する
- Vector3 同士の減算で差のベクトルを出し、Vector3.sqrMagnitude で距離の累乗を算出し、とても小さい数 float.Epsilon より小さければ Vector3 同士が同一であると見なして移動終了とする
//Co-routine for moving units from one space to next, takes a parameter end to specify where to move to. protected IEnumerator SmoothMovement (Vector3 end) { //Calculate the remaining distance to move based on the square magnitude of the difference between current position and end parameter. //Square magnitude is used instead of magnitude because it's computationally cheaper. float sqrRemainingDistance = (transform.position - end).sqrMagnitude; //While that distance is greater than a very small amount (Epsilon, almost zero): while(sqrRemainingDistance > float.Epsilon) { //Find a new position proportionally closer to the end, based on the moveTime Vector3 newPostion = Vector3.MoveTowards(rb2D.position, end, inverseMoveTime * Time.deltaTime); //Call MovePosition on attached Rigidbody2D and move it to the calculated position. rb2D.MovePosition (newPostion); //Recalculate the remaining distance after moving. sqrRemainingDistance = (transform.position - end).sqrMagnitude; //Return and loop until sqrRemainingDistance is close enough to zero to end the function yield return null; } }
Parse.com でバックグラウンドジョブの登録と実行メモ
Parse.com のダッシュボードからアプリの作成
- アプリ名は "JobTest" で作成
parse コマンドのインストール
$ curl -s https://www.parse.com/downloads/cloud_code/installer.sh | sudo /bin/bash
Cloud Code のディレクトリを作成
$ parse new JobTest
$ cd JobTest
バックグラウンドジョブの定義
- cloud/main.js にバックグラウンドジョブを定義する
- 今回は "JobLog" Object を1件作成するだけのジョブを用意した
- 記述方法は大体 JavaScript Guide の通りで、成功/失敗で status.success/status.error を返すように
Parse.Cloud.job("firstJob", function(request, status) { var JobLog = Parse.Object.extend("JobLog"); var jobLog = new JobLog(); jobLog.set("message", "Job is running."); jobLog.save(null, { success: function(jobLog) { status.success("Logging completed successfully."); }, error: function(jobLog, error) { status.error("Uh oh, something went wrong."); } }); });
定義したバックグラウンドジョブを配布
$ parse deploy
バックグラウンドジョブの実行
- Parse.com のダッシュボードから [Core] > [Jobs] を開き、右上の [Schedule a Job] からバックグラウンドジョブのスケジューリングを行う
動作確認
- ダッシュボードから [Core] > [Jobs] > [Job Status] でバックグラウンドジョブの成功不成功が確認できる
- firseJob が成功した場合 [Core] > [Data] で "JobLog" Object が作成されている
Unity で iOS ビルド時に Framework の自動追加・画像の自動追加・任意のファイルの登録・URL Scheme の設定をする
はじめに
- Unity で PostprocessBuildPlayer を使ってタイトルのようなことをする。
- 掲載のコードはGAMEFEATのSDKを組み込んだときのもの。
ビルド後に実行されるスクリプトの用意
- Assets/Editor/PostprocessBuildPlayer という名前で以下のスクリプトを配置する
#!/usr/bin/env ruby require 'xcodeproj' require 'fileutils' PROJECT = 'Unity-iPhone' TARGET = 'Unity-iPhone' LIBRARY = 'Libraries' # システムFramework追加 def add_frameworks(project, names, optional = false) project.targets.each do |target| next unless TARGET == target.name build_phase = target.frameworks_build_phase framework_group = project.frameworks_group names.each do |name| next if exist_framework?(build_phase, name) path = "System/Library/Frameworks/#{name}.framework" file_ref = framework_group.new_reference(path) file_ref.name = "#{name}.framework" file_ref.source_tree = 'SDKROOT' build_file = build_phase.add_file_reference(file_ref) if optional build_file.settings = { 'ATTRIBUTES' => ['Weak'] } end end end end # 外部Framework追加 def add_external_frameworks(project, names) project.targets.each do |target| next unless TARGET == target.name target.build_configurations.each do |configuration| # Framework Search Pathsを設定 configuration.build_settings['FRAMEWORK_SEARCH_PATHS'] = configuration.build_settings['LIBRARY_SEARCH_PATHS'] end build_phase = target.frameworks_build_phase library_group = project.main_group.children.find {|child| child.path == LIBRARY} names.each do |name| next if exist_framework?(build_phase, name) copy_library(name) path = "#{LIBRARY}/#{name}.framework" file_ref = library_group.new_reference(path) file_ref.name = "#{name}.framework" file_ref.source_tree = 'SOURCE_ROOT' build_phase.add_file_reference(file_ref) end end end # Framework追加済みか def exist_framework?(build_phase, name) build_phase.files.each do |file| return true if file.file_ref.name == "#{name}.framework" end false end # 外部FrameworkをUnityのディレクトリからXcodeのディレクトリへコピー def copy_library(name) asset_path = "#{ARGV[0]}/../Assets" from = File.expand_path("#{asset_path}/Editor/iOS/#{LIBRARY}/#{name}.framework", __FILE__) to = "#{ARGV[0]}/#{LIBRARY}/#{name}.framework" FileUtils.copy_entry(from, to) end ### =================================================== project_path = ARGV[0] + "/#{PROJECT}.xcodeproj" project = Xcodeproj::Project.new(project_path) project.initialize_from_file ### Framework の自動追加 ### # require で追加 add_frameworks(project, ["Foundation", "UIKit", "CoreTelephony"]) # optional で追加 add_frameworks(project, ["AdSupport", "StoreKit"], true) # 外部 Framework の追加 # 事前に Assets/Editor/iOS/Libraries にファイルを置いておく add_external_frameworks(project, ["GameFeatKit"]) ### 画像のコピー ### # 一度 XCode の Images.xcassets にファイルを追加し、作成されたファイルを Assets/Editor/iOS/Images に保存しておく asset_path = "#{ARGV[0]}/../Assets" FileUtils.cp_r("#{asset_path}/Editor/iOS/Images/gamefeat/", "#{ARGV[0]}/Unity-iPhone/Images.xcassets/") ### ライブラリのコピー ### # 任意のファイルを XCode に登録する files = [ # [ファイル名, コピー元, コピー先 ] [ "JSONKit.h", "#{asset_path}/Editor/iOS/Libraries/JSONKit/", "#{ARGV[0]}/Libraries/" ], [ "JSONKit.m", "#{asset_path}/Editor/iOS/Libraries/JSONKit/", "#{ARGV[0]}/Libraries/" ], ] files.each do |file| FileUtils.cp_r(file[1] + file[0], file[2]) project.targets.each do |target| next unless TARGET == target.name library_group = project.main_group.children.find {|child| child.path == LIBRARY} file = library_group.new_file(file[2] + file[0]) target.add_file_references([file]) end end ### URL Scheme の設定 ### plist = "#{ARGV[0]}/Info.plist" appname = "info.nirasan.ssec" system("/usr/libexec/PlistBuddy -c 'Add :CFBundleURLTypes array' #{plist}") system("/usr/libexec/PlistBuddy -c 'Add CFBundleURLTypes:0:CFBundleURLSchemes array' #{plist}") system("/usr/libexec/PlistBuddy -c 'Add CFBundleURLTypes:0:CFBundleURLSchemes:0 string #{appname}' #{plist}") ### 設定の保存 ### project.save
参考サイト
http://starzero.hatenablog.com/entry/2014/02/18/163330
http://akisute.com/2012/09/unity-postprocessbuildplayer-weak.html
http://qiita.com/kyubuns/items/6cbc982a3d96e8f4c5ce
https://github.com/CocoaPods/Xcodeproj
https://gist.github.com/larryaasen/5035313
http://qiita.com/kyubuns/items/8e5b56e51091aa5a48ba
Unity で Parse Config を使いアプリの設定をリモート管理する 〜 例えば審査中だけアプリの挙動を変化させる
はじめに
- Parse.com の Parse Config を使うと、アプリの設定を手軽にリモートで管理することが出来、例えば審査中だけ特定の GameObject を表示させないなどの処理が書きやすくなります。
- Parse Config は単純なキーバリューストアで、以下のような型が扱えます。
- string
- bool/int/double/long
- DateTime
- ParseFile
- ParseGeoPoint
- IList (even nested)
- IDictionary (even nested)
- Parse.com の導入は過去記事を参照。
審査中だけ特定の GameObject を非表示にする場合
Parse Confing の設定
- Parse.com のサイトから Core > Config で Parse Config の画面へ
- Parameter : "InReview", Type: Boolean, Value true でパラメータの作成
- ドキュメントは https://parse.com/docs/unity_guide#config
Unity 側のコード
public GameObject TargetObject; ParseConfig parseConfig = null; bool? inReview = null; void Start () { GetParseConfig (); } void Update () { if (inReview != null) { NGUITools.SetActive (TargetObject, !((bool)inReview)); inReview = null; } } void GetParseConfig () { ParseConfig.GetAsync ().ContinueWith (t => { if (t.IsFaulted) { // using cacehd config parseConfig = ParseConfig.CurrentConfig; } else { parseConfig = t.Result; } bool result = parseConfig.TryGetValue ("InReview", out inReview); if (!result) { // failed inReview = null; } }); }
NGUI の ScrollView 内のアイテムをドラッグアンドドロップで並び替える
はじめに
- Unity + NGUI 環境で ScrollView 内のアイテムをドラッグアンドドロップで並び替えられるように
構成
ゲームオブジェクトの親子関係とアタッチするコンポーネント
- Scroll View (UISCrollView, UIDragDropRoot)
- Grid (UIGrid)
- Item (BoxCollider, UIDragDropItem, UIDragScrollView)
- Item
- Item
- Grid (UIGrid)
Scroll View オブジェクトの設定
- UIPanel の Size を任意で設定
- UIScrollView の Movement を任意で設定(今回は縦スクロールにするので Vertical に)
Grid の設定
- UIGrid の Arrangment を(今回は縦スクロールなので)Vertical に
- 同じく Cell Width と Cell Height を任意で設定
- Sorting を(縦スクロールの場合は)Vertical に
Item の設定
- UIDragDropItem の Restriction を Horizontal に
並び替え終了時にリストのアイテムを取得する
- UIDragDropItem を継承し OnDragDropEnd を override することで、ドラッグアンドドロップ終了時の処理を記述できる。
- 下記のスクリプトを UIDragDropItem の代わりに Item にアタッチすると、ドラッグアンドドロップ終了時にリストの並び順通りにオブジェクトを取得して名前を表示する。
using UnityEngine; using System.Collections; public class MyDragDropItem : UIDragDropItem { protected override void OnDragDropEnd () { var list = mGrid.GetChildList (); list.ForEach (child => Debug.Log (child.name)); base.OnDragDropEnd (); } }