アプリ置き場

アプリ置き場

http://www.moreread.net/

MaDotPaint V1.0.0公開

MaDotPaint V1.0.0
 
ペイントツールを作ってみました。自分ツール。

 
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
_/_/ MaDotPaint
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
 
[Description]
2Dゲーム開発のお供に最適なペイントツールです。
操作手順が少なくて済むように設計されており、基本的な処理が簡単に実行できます。
表示倍率や表示位置の変更、トリミングなどをマウス操作だけで行うことができるため、高い作業効率を実現します。
また、画像サイズや、グリッド線を画面上に表示するため、確認作業にも適しています。
シンプルで軽量(たぶん)、MSペイントの代わりにどうぞ。
 
<2018/01/01> V1.0.0
First Release
 
[How to use]
左クリック:点描
左ドラッグ:矩形塗りつぶし(Shift+で線描画)
右クリック:スポイト
右ドラッグ:範囲コピー(コピー後は左クリックでペースト)
ホイール:表示倍率の変更
中ドラッグ:画像の表示位置変更(Ctrl+右ドラッグでも可)
 
画像の外側をスポイトすると、透過色を取ることができます。
パレットの各色をダブルクリックすると色を編集することができます。
パレットはEDGE形式のものを読み込むことが可能です。
 
[System Requirements]
Windows7 or later
.NET FrameWork 4.5 or later
 
[Uninstall]
Delete this folder.
 
[HP]
 
[Author]
nazenaninadesico : nazenaninadesico@gmail.com
 
[Credit]
デフォルトのパレットはこちらのMizさんのパレットを参考にさせていただきました。
 
ダウンロード先
 
なぜに?
久しぶりにゲーム開発をして、その中でさまざまな画像周りのツールにお世話になりました。しかしながら、操作性に納得がいくものがなく、また機能によって複数のアプリにまたがって利用が必要で煩雑。思えば10年以上前からそうでした。微妙にストレス。
ということで一念発起して、自分用ペイントツールを作ると決心しました。車輪の再発明感はんぱないけどしょうがないね。どこかには望みのツールがあったりするのかもだけど、見つからないんだ。
 
そして突貫で3日で作った。
とりあえずVectorに登録(10年以上使ってないアカウントを発掘)しといたけど、正月休みぽい。
 
セールスポイント

・画像サイズの数字が画面上に表示されるガイド付き。
 2Dゲームではドット単位の調整が必要なためあると便利。
・いつでもはじっこ(■のとこ)を掴んでトリミングやサイズ拡張が可能。
・マウスホイールで一瞬でズームイン、ズームアウト。
 画像の細部や全体像をぱぱっと確認できます。
・0.1倍まで縮小表示化。大きな画像の全体像をサクッと確認。
・高倍率のときはグリッド表示可能(打ち間違いを低減)。
・ミドルボタンドラッグ(又はCtrl+左ドラッグ)で画像の位置を変更可能。
 拡大中の面倒なスクロールバー操作とはお別れ!
・右クリックスポイトで、パレットまで移動しなくよい。
・範囲外をスポイトすると透過色をスポイト。
 そのまま透過色で描画すれば透過画像を生成可能。
 塗りつぶし、色置換、許容値の設定なども透過画像作成をサポート。
・右ドラッグで範囲コピー(そのまま左クリックでスタンプ可能)。
 クリップボードにも自動でコピーされます。
・左ドラッグで矩形塗りつぶし、描画効率アップ、消しゴム替わりにも。
・問答無用で32ビットPNG保存(他フォーマットは必要になれば追加予定)。
・単一ウィンドウアプリで管理が楽(MDIが苦手なだけ!)。
・MSペイント替わりにサクッと立ち上げられる軽量さ。シンプル!(低機能なだけ)
 
ターゲットは主にゲーム開発者(プログラマ)です。
グラフィッカーさんは、描画まわりが高機能なソフトを使ったほうが良いでしょう。
ゲーム開発の過程で、素材をトリミングしたり、加工したり、コードに落とす際に画像のサイズを確認したり、ちょっとした絵や仮画像を描いたり、スクリーンショット加工したり…といった場面で、高い効率性を実現します。たぶん……。加工系ツールと、ペイント系ツールの中間ぽいのかな。足りていない機能は徐々に追加していくつもり。飽きなければ!
 

アプリ公開 つづき

とりあえず全プラットフォームで公開されました。
Xamarin+CocosSharpなクロスプラットフォーム開発アプリ。
 
Android版は相変わらずダウンロードほぼゼロ。iOS版は無事公開されたけど、言語が英語になってるわ、カテゴリがアドベンチャーだわでどうしたらいいんだ。
PC版は「ふりーむ!」でレビューが2件もついてる!うれぴ!
モンスターランドやビックリマンワールドのアレなのでレトロゲーすきな方は是非プレイしてくださいまし。
 
 
iOS
 
PC版
 

 
さて、忘れないうちにまた何か作りたいものだけど。うーむ。

アプリ公開

初のAppStore申請。
①VisualStudioでReleaseビルドでipaファイルを生成する
iTunes Connectでアプリ追加したりバンドルID発行したりする
XcodeのApplicationLoaderからipaファイルをアップロードする
はじめてでよくわかっておらず、アップロードしようとするとエラーが発生。
ERROR ITMS-90161: "Invalid Provisioning Profile. ~
 
Provisioningファイルとやらが何かイマイチ理解してないけど、VisualStudioからは下記のところをDistributionにしたらなんかうまくいった気がする。

他にもアイコンの透過PNGやめーや的なエラーとかも出た。
とりあえず無事に審査待ちに。
 
Androidのほうは公開してから1日以上たってるんだけど、自分以外ダウンロード数0!
新着アプリって紹介されたりしないの?

どこからもリンクされないんじゃダウンロードされんわ。
どうすればいんだー。
 

Xamarin + CocosSharp を使ってみたい ⑧

iPadで画面の向きが変わってしまう問題
 
Info.plist

にチェックを入れたらiPadでも向き変更抑制が効くようになった。めでたし。
public override bool ShouldAutorotate()
{
    return true;
}
 
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations()
{
    return UIInterfaceOrientationMask.Landscape;
}
 
 
CocosSharpのフォント描画
 
フォントの読み込み方法がわからない。うぐぐ。
CCLabel label = new CCLabel(string, "/Resources/Fonts/hoge.ttf", font.size);
 
ふむ?
/Resource/Content/hoge.ttfとおいて、プロパティから「コンテンツ」を選択すればよさげ?
CCLabel label = new CCLabel(string, "hoge.ttf", font.size);
読み込めた!
 
が、読み込みがくっそ遅い。Labelの数が多いと使うのは不可能なレベル。
CCLabel生成すると、それぞれフォント読み込んでるくさいなぁ。
 
あとCCRenderTextureに描画しているんだけど、初回描画はアンチエイリアシングOFFにしてもぼやける。一度描画して文字列変えたらようやくOFFにある、なんじゃこりゃ。
 
文字列描画まわりもあやしいなぁ。
できるだけ画像リソースで文字列をもっておいて描画するしかないか。。。
 
 
ロケールの判断
 
iPhoneで常にen-USじゃねーか(怒)
CultureInfo.CurrentCulture //en-US
 
しょうがないのでiPhoneネイティブで取って渡す。
だんだん構造がきたなくなってくるぞお。
NSLocale.CurrentLocale.LocaleIdentifier //ja_JP
 
 
Android版のログ出力抑制
 
AndroidEnvironmentで設定すればいいらしい
MONO_LOG_LEVEL=message
 
 
広告対応
 
AdMob登録後、
をNuGetしてきて、適当にこのへん見たりして実装してみる。
 
must be called on the main UI thread.
っておこられた。RunOnUiThreadというのを使うらしい。
activity.RunOnUiThread(() =>
{
    if (interstitialAd != null && interstitialAd.IsLoaded)
    {
        interstitialAd.Show();
    }
});
 
Interstitial広告は閉じると再度ロードが必要らしい。
AdListener.OnAdClosedでもっかい作る。
public override void OnAdClosed()
{
    base.OnAdClosed();
    interstitialAd.Dispose();
    //requestInterstitialAd();
}
 
 
アプリ名(Android)
 
アプリ名がどこ設定していいかわからなくて悩む。Activityのラベルだった。
[Activity(Label = "ここ", MainLauncher = true, Icon = "@drawable/icon", AlwaysRetainTaskState = true, LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden)]
public class MainActivity : Activity
 
 
アイコンの作成
 
必要なアイコンをまとめて作ってくれるWebアプリ。すばらっ!
 
パワポでシートを正方形にして、適当に高解像度の画像を作る。
それをPNG出力して、先ほどのサイトに食わせると、30種類くらいの画像ができました。まじか、そんなに使うのか……。
 

 
 
Androidアプリのライフサイクル
 
ライフサイクルとか全然きにしてなかったけど、ふと戻るボタン押したらActivity死ぬじゃないの。初代Xperia時代から久しぶりの開発で、完全に忘れてた。
とりあえず、下記で戻るボタンをHOMEボタン化できた。
public override bool OnKeyDown([GeneratedEnum] Keycode keyCode, KeyEvent e)
{
    if (keyCode == Keycode.Back)
    {
        Intent homeIntent = new Intent(Intent.ActionMain);
        homeIntent.AddCategory(Intent.CategoryHome);
        homeIntent.SetFlags(ActivityFlags.NewTask);
        StartActivity(homeIntent);
        return true;
    }
    return base.OnKeyDown(keyCode, e);
}
 
あとは、Sleepボタン押したとき、他のアプリが動いたときにたまにActivityは死ぬ。
死んだら、CCSpriteやCCLabelなど、CocosSharpのUIコンポーネントのバッファがおかしくなってる。作り直さないとだめぽい。
もう完全にプロセス死んで再起動したほうがましなんだが……。なんでこんなゾンビ状態で動いてるの。プロセス殺す命令はAndroid5.0かららしい。4.xで動かしたいしなぁ。
 
 
ようやく
公開できそうなくらいにはなってきた。
くっそー1週間で作ったゲームの移植に3週間かかったなー。
iPhoneAndroidの知識の不足分を除いても、なかなかの地雷原ぷりでイニシャルコストは結構高いんじゃないかな。でもメンテは楽だと信じてる。あとやっぱC#だけで書けるのが大きい。

Xamarin + CocosSharp を使ってみたい ⑦

AndroidGC発生しまくりでカクカク問題
 
nursery-sizeをでっかく指定しておけばとりあえずGCを我慢してくれるようだけど。。。いろいろパラメータ指定したら、なんかときどき実行中にエラーでて落ちるように。
MONO_GC_PARAMS=bridge-implementation=tarjan,nursery-size=128m,soft-heap-limit=512m
 
The Xamarin.Android garbage collector can be configured by setting the MONO_GC_PARAMS environment variable. Environment variables may be set with a Build action of AndroidEnvironment.
The MONO_GC_PARAMS environment variable is a comma-separated list of the following parameters:
  • nursery-size = size : Sets the size of the nursery. The size is specified in bytes and must be a power of two. The suffixes k , m and g can be used to specify kilo-, mega- and gigabytes, respectively. The nursery is the first generation (of two). A larger nursery will usually speed up the program but will obviously use more memory. The default nursery size 512 kb.
  • soft-heap-limit = size : The target maximum managed memory consumption for the app. When memory use is below the specified value, the GC is optimized for execution time (fewer collections). Above this limit, the GC is optimized for memory usage (more collections).
  • evacuation-threshold = threshold : Sets the evacuation threshold in percent. The value must be an integer in the range 0 to 100. The default is 66. If the sweep phase of the collection finds that the occupancy of a specific heap block type is less than this percentage, it will do a copying collection for that block type in the next major collection, thereby restoring occupancy to close to 100 percent. A value of 0 turns evacuation off.
  • bridge-implementation = bridge implementation : This will set the GC Bridge option to help address GC performance issues. There are three possible values: old , new , tarjan.
  • bridge-require-precise-merge: The Tarjan bridge contains an optimization which may, on rare occasions, cause an object to be collected one GC after it first becomes garbage. Including this option disables that optimization, making GCs more predictable but potentially slower.
For example, to configure the GC to have a heap size limit of 128MB, add a new file to your Project with a Build action of AndroidEnvironment with the contents:
MONO_GC_PARAMS=soft-heap-limit=128m
 
.Androidプロジェクトにテキストファイルを追加して、プロパティウィンドウからタイプを
AndroidEnvironment
にすればよいらしい。要調整。
 

音が鳴らなくなる問題①
 
iOSバイスでしばらく動作テストしてると、必ず音が鳴らなくなって、そのうちフリーズ。ホワッツ
うそやろ……1024回で打ち止めって、しかも2年以上前から直ってないの?
たしかに1024回で「Failed to generate OpenAL data buffer:」と吐き出して、それ以上の再生が不可能になる。何かリークしてるのか?再生途中でストップをかけた分は回収されていて、寿命が延びてる気がする。
CCAudioEngine.SharedEngine.End();
とかしてみたが、特に意味はなかった。
 
自前でAudioエンジンのインスタンスを作ってみる。
CCAudioEngine engine =new CCAudioEngine();
 
~略~
 
engine.End();
engine.Dispose();
engine = new CCAudioEngine();
1024回をカウントして作り直してみるが効果なし。
が、なんということでしょう。CCAudioEngineを作り直したあとにGCかけると復活する。
System.GC.Collect();
 
AudioEngine作り直す度にBGMが止まったらうんこすぎるので、AudioEngineをBGM用とSE用を分けて2個にしてみる。おし、いけた。
余裕のあるタイミングでときどき初期化しとくほうがいいかなー。自前でインスタンス作ったから、PauseとResumeの伝達も自前になるんよね、たぶん。はぁ……。
 

音が鳴らなくなる問題②
 
iOSバイスでBGMが鳴らなくなってフリーズするのは別件だった。Audioは地雷原か……。
MP3をループで再生したところ、終了時点でおかしくなった。ループせず、以降のBGMの再生できず。午後のこ~だをひっぱりだしてきて、MP3→WAV→MP3と再変換してみたが直らず。データが悪いわけではなく、やはり何か問題を抱えてそうだ。
いくつか試した感じだとビットレートなどは関係なく、短めのBGMをループさせると、ちょくちょく再生終了時に死ぬ。誰も使ってないのかよ。
しょうがないので自前でループ機構を作る。終了を検知できないから、曲の再生時間を覚えておいて、フレームカウントから経過時間算出だよ!
いまのところちゃんと動いてる気がする。スリープ、レジュームすると狂いそうだなぁ。
 

固有実装
 
いざとなったらごりごり書くのか?書くしかないのか?
  • OnPlatform (XF 1.0 ~)
  • DependecyService (XF 1.0 ~)
  • Plugins for Xamarin
  • Custom Renderer (XF 1.0 ~)
  • Effects (XF 2.1 ~)
  • Native Embedding (XF 2.2 ~)
 

ジョイパッド対応
 
手元のGPD XDでデバッグしやすいようにパッド対応しようと思った。
あっさりできてうれぴ。Xamarinとはあまり関係ないけど。
どこにマッピングするのか試行錯誤がしやすいように割り当てを辞書に入れた。
// MainActivity.cs
public override bool OnKeyDown([GeneratedEnum] Keycode keyCode, KeyEvent e)
{
    if (dicPadmap.ContainsKey(keyCode))
    {
        Pad.getInstance().dwButtons |= dicPadmap[keyCode];//Padは独自クラス
        return true;
    }
    return base.OnKeyDown(keyCode, e);
}
 
public override bool OnKeyUp([GeneratedEnum] Keycode keyCode, KeyEvent e)
{
    if (dicPadmap.ContainsKey(keyCode))
    {
        Pad.getInstance().dwButtons&= ~dicPadmap[keyCode];
        return true;
    }
    return base.OnKeyUp(keyCode, e);
}
 
あとAndroidでステータスバーなしのフルスクリーン。Activityでセット。
Window.AddFlags(WindowManagerFlags.Fullscreen);
 

FPS計測
 
CocosSharpって簡単にFPSとる方法ってあるんだろうか?
よくわからないから自前で実装してみる。
どういう構造になっているかいまだによく理解してないが、VisitRendererとやらが実際に描画されたときに呼ばれるようだ。ここでカウントすればよさげ。
 
protected override void VisitRenderer(ref CCAffineTransform worldTransform)
{
    base.VisitRenderer(ref worldTransform);
 
    frameCounter.nextFrame();
}
 

最適化
 
GCの発生頻度を抑えるべくAndroidEnvironmentに加えて、CCNode系のクラスの生成/破棄をフレームごとに行うのをやめた。さすがに無理があったようだ。
そしたらGPD XD(2015年のミドルクラスくらい?)でも60FPSでるようになったのでこれで十分かな?

 

Xamarin + CocosSharp を使ってみたい ⑥

回転抑制
画面の向きを変えると表示位置がおかしくなる。
手持ちのAndroid機にいたってはフリーズ。どうすれば……。
→ めんどっちいからとりあえず横向き固定化だ!
 
ここを参考にまずiOS
UIViewControllerを継承したクラスにて
public override bool ShouldAutorotate()
{
    return true;
}
 
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations()
{
    return UIInterfaceOrientationMask.Landscape;
}
としてみるが、シミュレータでは期待通りに動作だが、実機(iPad mini)ではまったく効果なし。回転しまくり。わからん。
[Export("application:supportedInterfaceOrientationsForWindow:")]
public UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, IntPtr forWindow)
{
    return UIInterfaceOrientationMask.LandscapeLeft;
}
各所で見かけたAppDelegateのおまじないも効果なし。
 
解決の糸口がつかめぬまま数時間、あーでもないこーでもない。しかし、ふとiPhoneで試すと問題なく回転を抑制できてた。どういうこと……。iPadで回転しないゲームとかあるよね?iPadiPhoneで何が違うの……。iPadは一旦保留で。
 
Androidマニフェスト弄ればいけた。
<application android:label="RetroAction.Android" android:largeHeap="true">
    <activity
        android:name=".MainActivity"
        android:screenOrientation="landscape" >
    </activity>
</application>
ついでにandroid:largeHeap="true"というおまじないもつけておいた。
 
 
バックグラウンドからのレジューム対応
iOS(ipad mini4)で復帰時に音が戻らないことがある。なんで?
iphone7は問題ないぽいな。性能依存だろうか。
それとも、レジューム周りの処理は自分で書く必要があるのだろうか?
ふむふむ。Androidは勝手にやってくれると。
iOSはテンプレートにCCGameView.pause = true / false は書いてあったな。
 
CCGameViewの中を見るとちゃんとpauseのsetterでAudioのPauseとResumu呼ばれてる。うーん。
public bool Paused
{
    get { return paused; }
    set
    {
        if (gameStarted && paused != value)
        {
            paused = value;
            previousTicks = gameTimer.Elapsed.Ticks;
 
            if (paused)
            {
                AudioEngine.PauseBackgroundMusic();
                AudioEngine.PauseAllEffects();
            }
            else
            {
                AudioEngine.ResumeBackgroundMusic();
                AudioEngine.ResumeAllEffects();
            }
 
            PlatformUpdatePaused();
        }
    }
}
 
いろいろ試してたところ、Preloadしたら直った気がする。原因は不明。メモリ食いそうだからPreloadはSEだけにしてたんだけどまぁいいか。
CCAudioEngine.SharedEngine.PreloadBackgroundMusic(path);
 
端末再起動
テストしてるとiphoneが画面真っ黒操作不能になってびっくり。スリープボタン長押しもきかない。調べたら1段階上の強制終了として[音量下げボタン]+[スリープボタン]長押しがあるそうで。
 
動作を軽くする
CCDrawNodeは可能な限り再利用しつつ、矩形描画等は最小限に、CCLabelもDictionaryにキャッシュした。加えて、フレームレートを30FPSに下げた。
といっても、ロジックは60FPSで組んじゃったので描画だけ間引く。
そしたらそこそこ動いてるかな。
60FPSでぬるぬる動かすにはCocosネイティブ造りにしないと無理そうだ。
 

Xamarin + CocosSharp を使ってみたい ⑤

部分描画(画像の一部を切り出して描画する)
Sprite.TextureRectInPixels = new CCRect(sposx, sposy, sw, sh);
Sprite.ContentSize = new CCSize(sw, sh);
 
文字列の折り返し
CCLabel.Dimensions = new CCSize(w, h);
 
仮想キーパッド(てきとう)
protected override void AddedToScene()
{
var eventListener = new CCEventListenerTouchOneByOne();
eventListener.OnTouchBegan = CCEventListener_TouchBegan;
eventListener.OnTouchCancelled = CCEventListener_TouchEnded;
eventListener.OnTouchEnded = CCEventListener_TouchEnded;
AddEventListener(eventListener, this);
}
 
Dictionary<int, uint> keyDic = new Dictionary<int, uint>();
private Boolean CCEventListener_TouchBegan(CCTouch touch, CCEvent touchEvent)
{
if (button_left.BoundingBox.ContainsPoint(touch.Location))
{
keyDic[touch.Id] = BUTTON_LEFT;
pad.buttons |= BUTTON_LEFT;
}
elseif (button_right.BoundingBox.ContainsPoint(touch.Location))
{
keyDic[touch.Id] = BUTTON_RIGHT;
pad.buttons |= BUTTON_RIGHT;
}
return true;
}
 
private void CCEventListener_TouchEnded(CCTouch touch, CCEvent touchEvent)
{
if (keyDic.ContainsKey(touch.Id))
{
pad.buttons &= ~keyDic[touch.Id];
}
}
 
private void CCEventListener_TouchMoved(CCTouch touch, CCEvent touchEvent)
//TODO
}
 
iPhone実機の接続
なかなかiPhone実機で動かせなくてイライラ。3~4時間かかってしまった。
罠回避ポイント。
  • Info.plistの配置ターゲット
  • .iOSプロジェクト設定のビルド→サポートアーキテクチャ
  • iPhone認証後のMacエージェントの切断、再接続
  • バンドル識別子変更後のソリューションクリーンビルド
  • 他いろいろあったけど忘れた
とはいえなんとか環境は整ったので、あとは作り込むのみ。
 
iPhone実機でマルチタッチが効かない
View.MultipleTouchEnabled =true;
 
iPhone実機で透過PNGが透過されない
ググるiOSでは8bit pngは透過しない?まじか。いや、32bitでも透過されない場合があるぞ。
どうもirfanViewで作ったPNGがよろしくないようだ。透過色、背景色の設定によるんだろうか?
あたらめてirfanViewPNG変換をいろいろやってみると、色がおかしくなったりしてる。PNG変換まわりはなんかおかしい。標準仕様外の最適化でもしてるんじゃろか。違うソフトで変換するかー。
 
あと.iOSプロジェクトのプロパティにあるPNG画像を最適化するにチェックいれると透過できない場合があったのでこれも注意。
 
描画が重い

 
どうしよう、実機でもカックカク。
メインループは60回まわってきてても画面の描画が反映されてるのはかなり少なそう。ダメージを受けたときのエフェクトとして、1フレームごとに表示/非表示の点滅をしたら、消えっぱなしに。
いろいろオーバーヘッドあるけどバッファの解像度は320x180とめっさ小さいので、iPhone7なら余裕だろうと思ってたんだけどなぁ。
 
ちょいとしらべてみるとDrawNodeでDrawRectangleしまくってるのが遅い。画像描画したほうがましだった。描画自体が遅いのか、毎フレームのノードの生成が遅いのか、生成と破棄ともなうGCが遅いのかは調べてないけど。
がんばって最適化するかー。
 
描画のずれ
iPhoneで画像の描画が1ドットずれる。上側1ドットが表示されず、下側1ドットが2ドットに引き延ばされてる。0.5ドットずれてるとかそういうアレだろうか?厳密なドット単位の処理がきついなぁ。とりあえず0.5f足したらなおった気がする。