--------(--)

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
2012-11-29(Thu)

vibrations[Unity]

CG WORLD 12月号、Unity特集なので買いました。
  • 最初は、セットアップから
  • Autodesk製品との連携できるよ
  • 実例、四つ
  • Unity4.0紹介



サイバーコネクトツーの実例とかよかったです、でもwebで見れたり。
http://www.slideshare.net/cc2_ss/guiltydragon-artwork#btnNext
Unity4.0さわりたいなって思いました。

というわけで、
Android端末を振動させたいなと思って、調べてみました。

Handheld.Vibrate()

http://docs.unity3d.com/Documentation/ScriptReference/Handheld.Vibrate.html

確かに、Handheld.Vibrate()関数で振動させることは出来ましたが、
時間などの指定はできず、1秒くらい、ビーって振動してくれます。
もっと短く、ブビッて感じで振動させたいのですが。。。
これは神のpluginを作るようにとのお達しだと受け取りました。現状は全力で保留
軽く調べてみましたが、タダの奴は見つからなかったです。
とりあえず、上記の関数でバッテリーにも優しくない、長振動を与えておきます。
対応できました
スポンサーサイト
2012-11-27(Tue)

Swarm組み込み[Unity]

ランキングみたいなのは欲しいなと思って、ちょこちょこ調べてはいました。
まあOpenFeintで問題ないでしょ、みんな入れてるし、とか思っていた時期もありましたが、
組み込もうと思ったときには、既にお亡くなりになっていたので、
結局どれにしようかな? からスタートです。

参考にさせて頂きました
http://hamondiary.blogspot.jp/2012/11/openfeint.html

plugin自作とか無理ゲーなんで、Unity用のSDKがあることが求められています。
いくつか組み込まれているアプリをdownloadして動作見たりしましたが、割と適当にSwarmにしてみました。

swarm
BLOGの OpenFeint逝ったー! みたいな記事が気に入ったからです。

本当は、SDK入れてみて、割と簡単に組み込みテストは出来たので、
これでいいかなという流れです。

以下のような感じで組み込みました。leaderboardsしか使ってないです。
※割と組み込みドキュメントそのままです。。。

0.Sign Upで、Account作成
SDKなんかも、Account作らないとdownloadできなかったと思います。

1.Appの登録
ログインして、webページ(http://swarmconnect.com/admin/)から
App Nameを入れると、App ID / App Key が発行されました。

2.package import
Unity用のPluginをdownloadして、packageファイルを自分のプロジェクトにインポート
AndroidManifest.xml が含まれています、Plugins/Android以下に既にある場合は、
mergeが必要です。

3.script追加
Swarm.init と Login Listener を追加
initで 発行された AppId, AppKeyを記述します。
Login ListenerはLogin処理中のコールバッグなので、登録しなくてもSwarm.initのみで動きました。

どこかのAwake()で、

SwarmLoginManager.addLoginListener(delegate(int status) {
if (status == SwarmLoginManager.USER_LOGGED_IN) {
Debug.Log ("login ok!");
} else if (status == SwarmLoginManager.LOGIN_STARTED) {
Debug.Log ("login started!");
} else if (status == SwarmLoginManager.LOGIN_CANCELED) {
Debug.Log ("login canceled!");
} else if (status == SwarmLoginManager.USER_LOGGED_OUT) {
Debug.Log ("user logged out!");
}
});

どこかのStart()で、

public void login_swarm() {
if( Swarm.isInitialized() && !Swarm.isLoggedIn() ){
Swarm.showLogin();
} else if( !Swarm.isInitialized() ) {
Swarm.init(applicationId, applicationKey);
}
}

4.Androidmanifest.xml 更新
コメントに書いてある箇所を置換しました。
一ヶ所だけでないので注意
Replace "com.company.product" in with your package name --> パッケージ名
Replace android:icon="@drawable/icon" with the path to your app's icon --> @drawable/app_iconに
Replace android:label="Swarm Unity Demo" with your app's label --> @string/app_nameに

ここまでで、ログインできるようになるはず。

5.leaderboards追加
ログインして、webページから作成
Leaderboar Name と Decending(降順) or Ascending(昇順)
Score Formatなどを設定
自分は、Timeを保持します。
SubmitするとLeaderboardIdが発行されました。

6.script追加
SwarmLeaderboard.submitScore で score送る
SwarmLeaderboard.showLeaderboard で leaderboard表示
SwarmLeaderboard.submitScoreAndShowLeaderboard で score送ってleaderboard表示
それぞれ、発行された LeaderboardIdを記述します。

// leader board
public static bool submit_score(GameMode mode, float score) {
bool ret = false;

if( Swarm.isLoggedIn() ){
if( System.Enum.IsDefined(typeof(GameMode), mode) ) {
int lbid = leaderboardId[(int)mode];
SwarmLeaderboard.submitScore(lbid, score);
ret = true;
// MEMO:実際に送信できたかではない
}
}

return ret;
}

void show_leaderboard_base(GameMode mode) {
if( System.Enum.IsDefined(typeof(GameMode), mode) ) {
int lbid = leaderboardId[(int)mode];
SwarmLeaderboard.showLeaderboard(lbid);
}
}
public void show_leaderboard(GameMode mode) {
if( Swarm.isLoggedIn() ){
Instance.show_leaderboard_base(mode);
} else {
Instance.login_swarm();
lbLogin = true;
lbmode = mode;
}
}
    ※色々
  • leaderboard、Timeフォーマット指定しましたが、そのままfloatの値(秒)を送ると、変換されていました。
    1/1000secまで表示されたので、アプリ側の表示を修正しました。
    表示フォーマットの細かい指定は見当たらなかったです。
  • Kiss The Fishが、Customized Example。頑張れば自分で表示できるっぽい
    提供されるUnity Pluginだと、そこまでは無理そう。
  • 横方向のログイン画面ない?
    Kiss The Fishが、ゲームは横画面ですが、ログインは縦画面でした。
  • スコアの送信とか、送信できたかは取れないっぽい
    割と大体そういう物なのかも。。。

テストで動かすのは、とても簡単でしたが、
インターフェースの仕様というか、みんなどうしてんのとか調べたり、
こうしたいけど、出来ないの?とかの試行錯誤はぐだぐだやってやってました。
2012-11-22(Thu)

プレハブ生成に関して - Instantiating Prefabs[Unity]

Instantiatingの処理は、メモリ確保やら、Awakeで行っているGetComponentやらで、
重いはずなので、一部のprefabは、ゲーム開始時に一括Instantiate()しています。
あと、激しくガベコレしないようにとか。。。

同時に使われそうな分だけ、Instantiateして、activeをfalseにして寝かせておき、
instanceが必要なときに、activeしてもらって使用しています。

object pool とか言うらしいです。そんな大層なものだとは知りませんでした。

参考にしたコードは、AngryBotsです、
弾丸が確か、そうなってたよとなって思って、C#にしてそのまま使っています。
リスト検索などは簡易的ですが、自分にはこれで十分です。

using UnityEngine;
using System.Collections;

public class InstanceManager : MonoBehaviour {

static InstanceManager spawner;

public ObjectData[] caches;
Hashtable activeCachedObjects;

[System.Serializable]
public class ObjectData {
public GameObject prefab;
public int cacheSize = 10;

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

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

// Instantiate the objects in the array and set them to be inactive
for( int i = 0; i < cacheSize; i++ ){
objects[i] = MonoBehaviour.Instantiate(prefab) as GameObject;
objects[i].SetActiveRecursively( false );
objects[i].name = objects[i].name + i;
}
}

public GameObject GetNextObjectInCache() {
GameObject obj = null;

// The cacheIndex starts out at the position of the object created
// the longest time ago, so that one is usually free,
// but in case not, loop through the cache until we find a free one.
for( int i = 0; i < cacheSize; i++ ){
obj = objects[cacheIndex];

// If we found an inactive object in the cache, use that.
if( !obj.active ){
break;
}

// If not, increment index and make it loop around
// if it exceeds the size of the cache
cacheIndex = (cacheIndex + 1) % cacheSize;
}

// The object should be inactive. If it's not, log a warning and use
// the object created the longest ago even though it's still active.
if( obj.active ){
Debug.LogWarning (
"Spawn of " + prefab.name +
" exceeds cache size of " + cacheSize +
"! Reusing already active object.", obj);
InstanceManager.Destroy(obj);
}

// Increment index and make it loop around
// if it exceeds the size of the cache
cacheIndex = (cacheIndex + 1) % cacheSize;

return obj;
}
}

void Awake () {
// Set the global variable
spawner = this;

// Total number of cached objects
int amount = 0;

// Loop through the caches
for( int i = 0; i < caches.Length; i++ ){
// Initialize each cache
caches[i].Initialize ();

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

// Create a hashtable with the capacity set to the amount of cached objects specified
activeCachedObjects = new Hashtable(amount);
}

static public GameObject InstantiatePrefab(GameObject prefab, Vector3 position, Quaternion rotation) {
if( prefab == null ){
Debug.LogError("prefab must be set to point to a GameObject.");
}

ObjectData cache = null;

// Find the cache for the specified prefab
if (spawner) {
for (int i = 0; i < spawner.caches.Length; i++) {
if (spawner.caches[i].prefab == prefab) {
cache = spawner.caches[i];
break;
}
}
}

// If there's no cache for this prefab type, just instantiate normally
if( cache == null ){
return MonoBehaviour.Instantiate(prefab, position, rotation) as GameObject;
}

// Find the next object in the cache
GameObject obj = cache.GetNextObjectInCache();

// Set the position and rotation of the object
obj.transform.position = position;
obj.transform.rotation = rotation;

// Set the object to be active
obj.SetActiveRecursively(true);
spawner.activeCachedObjects[obj] = true;

return obj;
}

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

}

テーブルにprefabと個数を登録しておけば、Awakeでcacheしてくれます。

よい点は、
実行中に負荷の重いInstantiateしなくなり、メモリの確保の増減が抑えられます。

悪い点は、
Awake, Startなどでの初期化が使えないので、
自前で呼ばなくてはならず、それは忘れる可能性があることを意味します。
また、変数未初期化で前のobjectの状態が残ったりとか、分かりにくいバグを仕込めます。

prefabが登録されていなければ、通常通り instance作られるので、
登録していないprefabもInstanteate/Destroyする時は、必ずこのclassから行っています。
もし、キャッシュしたくなったら、prefab登録すればよいのでいい感じ。

cacheが足りなかったときのどうするかは迷ったのですが、結局使っているものを勝手に破棄して割り振ってます。
instanceを追加して、cacheを伸ばすか迷いました。
リリース時には、必要な数はfixしているはずなので、理論上はどっちでもいいはず。

cacheを伸ばす方が柔軟な気がしますが、バグったときは、垂れ流しになるので、
上限が切って、それ以上確保しない方が堅くてよいかなという判断です。
バグったときはどっちにしろ挙動は怪しくなるはずですが、死ぬよりはよいはず。
2012-11-21(Wed)

画面ちらつく、原因分からん[Unity]

Application.LoadLevel関数で、Scene移動を行っていますが、
Scene移動後、移動先のSceneで Application.Quit() をすぐに呼び出すと、
画面がちらついてしまっています。
具体的には、ゲーム中から、backボタン連打。前のSceneと移動先のSceneの画面がびかびかする感じ。

ちらついてしまう原因がよく分からないです。
とりあえずほんのちょっとだけ待ちをいれて、ちらちらしないようにだけ対処しました。
別でなんか激しく間違っているのかしら。

対処いれてみましたが...
2012-11-20(Tue)

メモリの使用量表示を追加[Unity]

これやったら、メモリ食うのかな?みたいなのがあった気がしたので、
調べていたら、メモリの使用量表示を見つけたので、組み込みました。

http://hideapp.cocolog-nifty.com/blog/2012/11/unity-action-31.html
http://wiki.unity3d.com/index.php/AllocationStats

割と AllocationStats そのままです。
変更箇所は、fpsの更新間隔設定できるようにと、表示をguiTextに移しただけです。

using UnityEngine;
using System.Collections;

//[ExecuteInEditMode ()]
[RequireComponent (typeof (GUIText))]
public class AllocationStats : MonoBehaviour
{
public float updateInterval = 0.5f;

private float accum = 0.0f; // FPS accumulated over the interval
private float frames = 0; // Frames drawn over the interval
private float timeleft; // Left time for current interval
private float fps = 0.0f;

void Start () {
useGUILayout = false;

if( !guiText )
{
print ("FramesPerSecond needs a GUIText component!");
enabled = false;
return;
}
timeleft = updateInterval;
}

void Update()
{
// FPS
timeleft -= Time.deltaTime;
accum += Time.timeScale/Time.deltaTime;
++frames;

// Interval ended - update GUI text and start new interval
if( timeleft <= 0.0 )
{
// display two fractional digits (f2 format)
//guiText.text = "" + (accum/frames).ToString("f2");
fps = (accum/frames);
timeleft = updateInterval;
accum = 0.0f;
frames = 0;
}

// AllocationStats
int collCount = System.GC.CollectionCount (0);

if (lastCollectNum != collCount) {
lastCollectNum = collCount;
delta = Time.realtimeSinceStartup-lastCollect;
lastCollect = Time.realtimeSinceStartup;
lastDeltaTime = Time.deltaTime;
collectAlloc = allocMem;
}

allocMem = (int)System.GC.GetTotalMemory (false);

peakAlloc = allocMem > peakAlloc ? allocMem : peakAlloc;

if (Time.realtimeSinceStartup - lastAllocSet > 0.3F) {
int diff = allocMem - lastAllocMemory;
lastAllocMemory = allocMem;
lastAllocSet = Time.realtimeSinceStartup;

if (diff >= 0) {
allocRate = diff;
}
}

guiText.text = "";
guiText.text += (allocMem/1048576F).ToString ("0") + " / " + ((peakAlloc/1048576F).ToString ("0"));
guiText.text += "mb";

guiText.text += (" (last ");
guiText.text += ((collectAlloc/1048576F).ToString ("0"));
guiText.text += ("mb:") + (lastDeltaTime.ToString ("0.000")) + ("s:");
guiText.text += ((1F/lastDeltaTime).ToString ("0.0"));
guiText.text += ("fps)\n");
if( Debug.isDebugBuild ){
guiText.text += (Profiler.usedHeapSize/1048576).ToString("0") + "mb\n";
}

guiText.text += ("rate ");
guiText.text += ((allocRate/1048576F).ToString ("0.0"));
guiText.text += ("mb\n");

guiText.text += ("freq ");
guiText.text += (delta.ToString ("0.00"));
guiText.text += ("s\n");
guiText.text += (fps).ToString("f2") + "\n";
}

private float lastCollect = 0;
private float lastCollectNum = 0;
private float delta = 0;
private float lastDeltaTime = 0;
private int allocRate = 0;
private int lastAllocMemory = 0;
private float lastAllocSet = -9999;
private int allocMem = 0;
private int collectAlloc = 0;
private int peakAlloc = 0;
}

カジュアルゲームなんで、全然平気そうだけど、モニターしとくと何か変なの入れてしまったときに、気づけると思います。きっと。

AllocationStats
2012-11-18(Sun)

アスペクト比設定(2) - NUGUIに対して[Unity:NGUI]

Camera.rectを設定して、アスペクトを固定、解像度が異なる端末でも同じように見えるように設定しています。
アスペクト比設定(1)

NGUIの解像度あわせに関して、以下を参考にしたりしたのですが、
http://udasankoubou.blogspot.jp/2012/08/unityngui2.html
画面外のスプライトが表示されて、具合が悪かったので以下の対応にしました。

201231118_camera1.jpg

NGUIのカメラに対しても、Camera.rectを設定して画面サイズを合わせる。
具体的には、UI Root下のUI Cameraが設定されているカメラを
アスペクト比を固定するスクリプトで、基準にするアスペクト比に固定。
(自分の場合は、16:9になるように、CameraUtilityのfixedCameraに設定)

このとき、Inspector上で以下の固定値を設定しておく
UIRootの Manual Heightには、基準とする解像度を設定(自分の場合は1280)
AutomaticはOFFに設定
※NGUIのversionで動作異なるのかも。。。
201231118_camera2.jpg 201231118_camera3.jpg

NGUIの解像度調整機能が縦(Height)にしか対応していない
とわれていますが、UI Cameraのビューポートに合わせて高さを調整してくれるみたいなので、
UI Cameraのビューポートを目的のアスペクト比に設定するだけで、
画面におさまってくれます。ビューポート設定されるので、画面外も消えてくれ、
NGUI用に別スクリプトも必要ないので、簡単!
2012-11-16(Fri)

FingerGesturesとの決別[Unity:Assets]

結局、決別しました。

fingergestures.jpg

FingerGesturesでタップ、スワイプ判定していましたが、
入力したつもりなのに判定が取れない場合結構あって、調べてました。
調べ始めると、このパラメータなんだっけて、いつもなってしまい、
なかなか奥まで入りこめずに、結局あきらめてしまいました。。。

マルチタッチやピンチアウト判定とかが必要なわけではないので、いらないよ。

temple runの入力をみてみると、スワイプは指を離したときに判定ではなく、
指を動かした時点で判定して、指を離さないと再判定が行われないようになっていました。
スワイプというより、むしろドラッグです。
ドラッグとタップなら判定いけるっしょと思って、旧判定のソースを戻して修正したら、
いい感じにまとまってくれました。

そもそも本当にAssets必要か考えないと駄目ですよね。
動作が理解できていないAssets使うのは、それなりにリスクはあります。
とにかく面倒くさいから使っちゃおうとすると、今回みたいに使った方が面倒くさくなったりするんですね。
もちろん複雑なGestureを判定しようとする場合は、使うべきだと思います。
2012-11-13(Tue)

アスペクト比設定[Unity]

Androidでゲームを作ろうとしていますが、
様々な端末があり、画面解像度も様々でどのように対応すべきか決まっていませんでした。
カメラの調整やら、2D配置やらが無駄になるので、
早めにどうするか決めておく必要があると思います。自分は決めていませんでしたが。。。

調べた結果、アスペクト比を固定することにしました。
端末のアスペクト比にかかわらず、基準にするアスペクト比に固定してしまって、
残りの部分は、黒縁。
ゲームによっては、複数の解像度に対応させて、
カメラや2Dの表示位置を切り替えたりしているものもあると思います。
自分では、複数のアスペクト比で調整などできないので、アスペクト比固定です。

基準とする解像度は、720p(16:9) に しました。
なんとなく決めました、自分が確認している端末がSC-04Dなのと、
iphone5が16:9なので、なんとなくです。

コード自体は、camera.rectを設定してあげるだけです。
後は、背景用のなにもうつさないカメラを後ろに置いて、黒縁にしました。

// アスペクト比を固定
public class CameraUtility : MonoBehaviour {

#region(inspector settings)
public int fixWidth = 1280;
public int fixHeight = 720;
public bool portrait = false;
public Camera[] fixedCamera;
#endregion

public static float resolutionScale = -1.0f;

void Awake() {
int fw = portrait ? this.fixHeight : this.fixWidth;
int fh = portrait ? this.fixWidth : this.fixHeight;

// camera
if( this.fixedCamera != null ){
Rect set_rect = this.calc_aspect(fw, fh, out resolutionScale);
foreach( Camera cam in this.fixedCamera ){
cam.rect = set_rect;
}
}

// MEMO:NGUIのmanualHeight設定は不要、
// UI Root下のカメラのアスペクト比固定すればよい
// UI RootのAutomaticはOFF, Manual Heightは想定heightを設定する

// アスペクト比を設定のみなので、設定後は削除
this.Destroy(this);
}

// アスペクト比 固定するようにcameraのrect取得
Rect calc_aspect(float width, float height, out float res_scale) {
float target_aspect = width / height;
float window_aspect = (float)Screen.width / (float)Screen.height;
float scale = window_aspect / target_aspect;

Rect rect = new Rect(0.0f, 0.0f, 1.0f, 1.0f);
if( 1.0f > scale ){
rect.x = 0;
rect.width = 1.0f;
rect.y = (1.0f - scale) / 2.0f;
rect.height = scale;
res_scale = (float)Screen.width / width;
} else {
scale = 1.0f / scale;
rect.x = (1.0f - scale) / 2.0f;
rect.width = scale;
rect.y = 0.0f;
rect.height = 1.0f;
res_scale = (float)Screen.height / height;
}

return rect;
}
}

これをやって良かったのは、
http://tasogare66.blog.fc2.com/blog-entry-5.html
で書いた、Unity Editor上でプレビュー確認するとき、ゲーム画面のプレビューを最大化しないとアスペクト比がずれる問題が解決され、実行すれば現状のウィンドウでアスペクト比を固定するように表示されるので、配置などの調整がやりやすくなりました。
2012-11-11(Sun)

データの保存関連(3)[Unity]

Unity、AndroidでのPlayerPrefsの保存場所を確認しました。

AndroidのPreferenceとして保存されていました。
なので、端末での保存場所は以下、

/data/data/"パッケージ名"/shared_prefs/"パッケージ名".xml

パッケージ名前は、Edit > Project Settings > PlayerSettingsで
Androidの項目で、 Bundle Identifier

adb shellで中身確認しました、xmlのkey valueのデータ

中身の改変は簡単なので、
勝手に変更されると危険なデータは、入れないほうがよさげ。
2012-11-09(Fri)

FingerGestures使っています[Unity:Assets]

ゲーム部分はまだまだですが、実機でプレイしてプレイ感覚なんかを確認するようにしています。
→ なんか疾走感が足りないな、どうすればいいんだろう
→ 疾走感云々いう前につまんなすぎるだろ、これ
→ スワイプ、タップの判定が糞すぎてストレスたまるんだよ
→ Inputの判定、FingerGesturesでやってるけど、こいつがよくないんだよ、きっと、そうだよ!
→ 使えるように入れただけで、調整もなにもしてないけどね!

最初はスワイプ判定、自前でやっていたのですが、
そのときは判定もっと糞だったので、
捨てて、結局 Asset使ってます。ごめんなさい。

fingergestures.jpg

ドキュメントとか調べました。
http://fingergestures.fatalfrog.com/wiki/doku.php
なんか Under construction とかあって残念な気持ちになります。

調べていて気づいたのですが、取得できる入力の速さや設定にあるlengthなんかは、
端末の解像度の影響受けるのですね。
http://iphone3gsapplication.blog129.fc2.com/blog-entry-220.html
どうりで実機で動かしている矢印、速いと思ったよ。。。

とりあえず基準とする解像度と端末の解像度から割合だして、必要そうな箇所を変換するようにしました。
Screen.dpiとかでやった方がいいかも。。。

スワイプが判定開始の距離が設定できるのですが、この辺の値を調整して受け付けをゆるくしてみました。

また、方向の判定が45度で区切られていないのを調整したつもり。
SwipeGestureRecognizer.DirectionTolerance
http://fingergestures.fatalfrog.com/releases/latest/apidoc/html/9ffea6a7-968c-93ef-85a1-c53c4a4ef97a.htm
Amount of tolerance when determining if the finger motion was performed along one of the supported swipe directions. This amount should be kept between 0 and 0.5f, where 0 means no tolerance and 0.5f means you can move within 45 degrees away from the allowed direction

コメントに 0.5にすると45度範囲になるよって書いてあったので設定してみましが、なんか判定違う気がする。。。

コード見てみると、

public static SwipeDirection GetSwipeDirection( Vector3 dir, float tolerance )
{
float minSwipeDot = Mathf.Clamp01( 1.0f - tolerance );

if( Vector2.Dot( dir, Vector2.right ) >= minSwipeDot )
return SwipeDirection.Right;

if( Vector2.Dot( dir, -Vector2.right ) >= minSwipeDot )
return SwipeDirection.Left;

if( Vector2.Dot( dir, Vector2.up ) >= minSwipeDot )
return SwipeDirection.Up;

if( Vector2.Dot( dir, -Vector2.up ) >= minSwipeDot )
return SwipeDirection.Down;

// not a valid direction
return SwipeDirection.None;
}

0.5だと60度じゃない?ドキュメント嘘付いてない?
左右が先に60度の範囲で判定されるので、上下の角度が狭くなるよ。。。
なので、45度になるように minSwipeDot √2/2=0.707位 なので toleranceは0.292894位?

少しは、判定ましになった気がしますが、
まだ微妙にスワイプも漏れたり、タップが効かなかったりする場面が多いので、
どこではじかれてんのか、調べる必要があります。

なにが言いたいかというと、FingerGesturesより、もっと安くていいAssetあるんじゃない?ってことです。
使ってみたり、評判確認したりしないと分からないよね。。。
2012-11-06(Tue)

データの保存関連(2)

ローカルのタイムランキングの更新処理を行い、
そのデータをsave/loadするように対応しました。
以下の様な感じ。
  • serialized versionをPlayerPrefsの一つのkeyに保存して、
    load時、versionを確認して動作を切り替えられるようにした。
    - serialized versionのkeyがなかったら、新規にデータを作成する
    - version同じだったら、loadを行う
    - versionが古いものだったら、データ更新の処理が差し込めるように
    - versionが新しいものだったら、なんかおかしいので、以降データ上書きはしないように

    void init_game_data() {
    string versionKey = "version";
    if( !PlayerPrefs.HasKey(versionKey) ){
    // データないので作成する

    // 一旦全削除
    PlayerPrefs.DeleteAll();
    // version
    PlayerPrefs.SetInt(versionKey, GameDefs.serializedVersion);
    // その他、全データ
    this.save_all();

    } else {
    int data_version = PlayerPrefs.GetInt( versionKey );
    if( data_version == GameDefs.serializedVersion ){
    // データ読み込む
    this.load_all();

    } else if( data_version > GameDefs.serializedVersion ){
    // データの引継ぎ処理

    } else {
    // newer version のデータ
    // 書き込みを禁止しておく
    disableRanking = true;
    disableOptions = true;
    }
    }
    }

  • ランキングデータ追加、ソートして保存できるように
    ソートとかいつもどうだっけってなります、結構手間取りました。

    [System.Serializable()]
    public struct RankingData : System.IComparable {
    public float laptime;

    public RankingData(float lt) {
    this.laptime = lt;
    }

    public int CompareTo( object dat ){
    RankingData otherDat = (RankingData)dat;
    if( this.laptime < otherDat.laptime ){
    return -1;
    } else if( this.laptime > otherDat.laptime ){
    return 1;
    } else {
    return 0;
    }
    }
    }

    if( rank_dat.tbl[lastid].laptime > ltime ){
    // 登録
    rank_dat.tbl[lastid].laptime = ltime;
    // sort
    System.Array.Sort(rank_dat.tbl);
    // save
    this.save_ranking(mode, true);
    ret = true;
    }

  • 後、PrefsSerialize.Loadに、try-catch追加しました

  • 起動時の初期化を行うようにして、そこでloadや新規ファイル作成とか行うようにしました。
    データのロードは起動時のみです。
    ロードで失敗した場合、新規データで上書きするとデータ消失してしまうので、
    以降は上書き保存しないようにしました。

失敗した場合、ダイアログなんかで通知があったほうがいいな、どうしよう。
ゲームデータの例外処理に関して、みんなどうしているのかな?
2012-11-05(Mon)

データの保存テスト、PlayerPrefsを用いたSerialize[Unity]

データの保存周りをテスト中です。
自前でファイルに保存するか、PlayerPrefsで保存するか悩んだ結果、
お手軽にPlayerPrefsを利用して保存することにしました。

preferences は、初期設定 なので大きいデータを保存するのには向かないですが、
今回保存するデータは、少しの設定データと簡単なローカルランキングのみなので、
これでよいかなと思って、動作テストまでを行いました。

PlayerPrefsの保存場所
Windowsでは、レジストリみたいです、
コンピューター\HKEY_CURRENT_USR\Software\[company name]\[product name]
レジストリに保存されることに対する嫌悪感はありましたが、
PlayerPrefs.SetString やる前に レジストリ確認すると、なんかもう勝手に格納されていたので、
吹っ切れてPlayerPrefsで、いいやってなりました。
Androidは、どうなっているのか情報が拾えていません。。。

http://docs.unity3d.com/Documentation/ScriptReference/PlayerPrefs.html

テストコードは以下のような感じです
ランキングデータは配列なので、Serializableなclassかstructを渡すと
それを一つのkeyで保存できます。

Android上でも動作確認しました。
コマンドプロンプトから、
> adb logcat
で unityのDebug.Log表示から、動作確認できました。

http://www.unifycommunity.com/wiki/index.php?title=ArrayPrefs2
http://d.hatena.ne.jp/tiri_tomato/20120920/1348119252
2012-11-02(Fri)

11月に入ったので。。。

残りの作業を洗い出して方針を考えました。

Androidで動作するカジュアルゲームをUnityで作成しています。
とにかく、一通りゲーム的な要素をそろえて、年内にリリースすることを目標にしています。
細部にこだわっていると、きりがないので、
とにかく全体に手をつけながら、徐々に質を上げていく方針です。
なので、初回実装は、しょぼくても気にしない!どうせ壊してやり直すかもしれないしね。

インターネットでは、Unityで1週間で作っちゃいましたとか、
カジュアルゲームなら大体2,3日で作っちゃいますよ、とかいう記事を見かけますが、
日曜プログラマーなんで、時間かかっても、とにかく完成を目指してやって行こうと思っています。

このゲームのリポジトリ自体は、7月末に作成していました。
それから一ヶ月は寝かしておいて、まともに作業を始めたのは9月末からです。
記録を残そうと思って、ブログを書き始めたのが、10月末。
ゲーム部分も、わりと未調整で突き進んでいます。とにかく出来ている感じを醸し出すと、
不思議とやる気も沸いてくるので、今はそこに入れるように手を付けていっています。
細かいことは、後からでいいんだよきっと。

考えられる残作業は以下です、

・ゲーム開始前のカウントダウン
・ゲーム中のBGどうするか
・リザルト画面
・ランキング対応
・データの保存対応
・ゲーム中の2D、NGUIで設置するように
・サウンド関連どうするか
・リリースに必要なこと、署名入りビルドやアイコンなど

11月は、それぞれ手をつけて、
12月に調整と気になるところの修正、バグ修正というざっくりスケジュールでいけるかな?

とりあえずタイトルメニューをNGUIで作りました、
病的でボタン認識もしづらいですが、とりあえず。。。
次は試行錯誤が必要なさそうな、データ保存対応をやっていこうかな。

title test

Advertising


カテゴリ
記事一覧
最新記事
最新コメント
最新トラックバック
月別アーカイブ
プロフィール

tasogare66

Author:tasogare66
ひっそり週末プログラム
@tasogare66

【Androidアプリ】


Wipes Arrow
(スワイプアクション)

review site
Uni本
リンク
RSSリンクの表示
FC2カウンター
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。