【Unity】 【有料Assets】 Simple Bones Animationを使って適当に取ってきたキャラにオリジナルアニメーションを作る
お疲れ様です。ぐーるです。
お久しぶりになってしまった。
サラリーマンや学生も期の変わりは忙しいんですよね。
Simple Bones Animation
Unity Asset Storeでいい感じのキャラを見つけた!
早速使ってゲーム作るぞ!
走りはこれで、攻撃はこれで…
あれ、ゲームクリア時になんかポーズしたいのにいい感じなの無いじゃん!
なんてことがあった時、3Dモデルが作れない自分を呪い諦めていたんですが、
どうやらこんな便利なツールがあると汗人柱さんが記事にしてたのを見つけました。
(ちょっと古い記事ですが。ほんと神)
おお、これイイじゃん、早速使ってみよう!と思いたち、買いました。
(有料です。4.95ドル…!)
事前準備
キャラクターはこいつを使います。
可愛い宇宙服シューターです。ライトなSFシューティングのプレイヤーにぴったり。
だが
- Idle
- Run
- Run Back
- Left
- Right
しかアニメーションが無く、実際に撃つようなモーションはありませんでした。
(2018/04/13。updateで追加されたりするかもしれません。)
これに撃つアニメーションを作りたいと思います。
新規プロジェクトを立ち上げ、Simple Bones AnimationとSpace ManをImportしておきます。
2.space_map_modelのPrefabを配置する
配置したらAnimatorを削除し、Animationに付随しているAnimation Clipを一旦全て外す。
これで新たなAnimation Clipを作ることができるようになります。
space manのRootを選択して、Add ScriptからSimple Bonesスクリプトを追加する
Root Nodeが空の状態になるので、Space Manの Pelvisをセットする。
ここまでやるともうボーンがSceneビューに見えるはず。
3.Shot.animを作成
Animationが空っぽの状態でAnimationビューを開くと 新たなAnimation ClipがCreate出来る。
Createを押下して、Shot.animとしてanimファイルを作成
すると、Simple BoneのAnimationにShotが選択されている状態になる。
これで準備完了です。
4.アニメーションを作る
SimpleBonesのAnimationから「Create Animation Keys」にあるPosition , Rotation , Scaleのいずれかを押下すると
ボーン周りのTransformがAnimationビューに紐付けることが出来る。
が、今回は使いません!
初期ポーズはIdleからコピーして持ってきます。
そして動かしたいパーツを選択しつつ…動かして…Add Key!!
間違えたら…一度消して、直して再度Add Key!!
5.Shotアニメーションが作れました
こんな感じでShotアニメーションが出来ました。
簡単でしょう?と言いたいですが、自然なボーンアニメーション作るのは難しいですね…。
でもツールはほんと凄い。有料は優良なんですね!
【Unity】Rigidbodyの弾道予測線をシュミレートする
前回の続きで、今回は撃つ側。
RigidbodyにAddForceを加えた場合の移動位置をシュミレートして表示する
なんていうのはみんなやっているようで、ググると大量に出てきます。
そのまま使わせて頂くのもOKですが、自分なりにまとめたソースもメモ。
Rigidbodyの移動を予測計算
using System.Collections; using System.Collections.Generic; using UnityEngine; /** * Rigidbodyにvecを加えた時の弾道をシュミレート。 */ public class BallSimulator : MonoBehaviour { [SerializeField] private GameObject _ballSimPrefab; // 何でもOK。予測位置を表示するオブジェクト private const int SIMULATE_COUNT = 20; // いくつ先までシュミレートするか private Vector3 _startPosition; // 発射開始位置 private List<GameObject> _simuratePointList; // シュミレートするゲームオブジェクトリスト void Start () { Init(); } void Update () { // デバッグ用に線を出してみる。必要無いなら無くても問題なし。 if (_simuratePointList != null && _simuratePointList.Count > 0) { for (int i = 0; i < SIMULATE_COUNT; i++) { if (i == 0) { Debug.DrawLine(_startPosition, _simuratePointList[i].transform.position); }else if (i < SIMULATE_COUNT) { Debug.DrawLine(_simuratePointList[i - 1].transform.position, _simuratePointList[i].transform.position); } } } } // 初期化 private void Init() { if (_simuratePointList != null && _simuratePointList.Count > 0) { foreach (var go in _simuratePointList) { Destroy(go.gameObject); } } // 位置を表示するオブジェクトを予め作っておく if (_ballSimPrefab != null) { _simuratePointList = new List<GameObject>(); for (int i = 0; i < SIMULATE_COUNT; i++) { var go = Instantiate(_ballSimPrefab); go.transform.SetParent(this.transform); go.transform.position = Vector3.zero; _simuratePointList.Add(go); } } } /** * 弾道を予測計算する。オブジェクトを再生成せず、位置だけ動かす。 * targetにはRigidbodyが必須です **/ public void Simulate(GameObject target , Vector3 _vec) { if (_simuratePointList != null && _simuratePointList.Count > 0) { // 発射位置を保存する _startPosition = target.transform.position; var r = target.GetComponent<Rigidbody>(); if (r != null) { // ベクトルはmassで割る Vector3 force = (_vec / r.mass); //弾道予測の位置に点を移動 for (int i = 0; i < SIMULATE_COUNT; i++) { var t = (i * 0.5f); // 0.5秒ごとの位置を予測。 var x = t * force.x; var y = (force.y * t) - 0.5f * (-Physics.gravity.y) * Mathf.Pow(t, 2.0f); var z = t * force.z; _simuratePointList[i].transform.position = _startPosition + new Vector3(x, y, z); } } } } }
発射する側は
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; /** * 弾を発射する */ public class BallShooter : MonoBehaviour { [SerializeField] private BallSimulator _ballSimurator; // 弾道予測線 [SerializeField] private Slider _powerSlider; // 力を変えるスライダー [SerializeField] private GameObject _shooterBase; // 土台 private Vector3 _firstPosition; private Rigidbody _rigidbody; private Vector3 _touchDownPos; private bool _isShot; // Use this for initialization void Start () { Init(); } // Update is called once per frame void Update () { if (!EventSystem.current.IsPointerOverGameObject()) { if (Input.GetMouseButtonDown(0)) { _touchDownPos = Input.mousePosition; } else if (Input.GetMouseButton(0)) { var tempPos = Input.mousePosition; Vector3 value = Vector3.zero; value.x = (_touchDownPos.x - tempPos.x); value.y = (_touchDownPos.y - tempPos.y); value.z = 0; _touchDownPos = tempPos; var qot1 = Quaternion.AngleAxis(value.x, new Vector3(0, 1, 0)); var qot2 = Quaternion.AngleAxis(value.y, new Vector3(1, 0, 0)); _shooterBase.transform.rotation *= qot1; this.transform.rotation *= qot2; } } Debug.DrawLine(this.transform.position, this.transform.position + this.transform.forward , Color.blue); // 発射中はシュミレートを止める if (!_isShot) { var vec = this.transform.forward * _powerSlider.value; _ballSimurator.Simulate(this.gameObject, vec); } } public void Init() { _firstPosition = this.transform.position; _rigidbody = this.GetComponent<Rigidbody>(); _rigidbody.isKinematic = true; _powerSlider.value = 10.0f; _isShot = false; } //-------------------------------------------------------------------------- // 発射(Buttonから呼ぶとか) public void Shoot() { _isShot = true; var vec = this.transform.forward * _powerSlider.value; _ballSimurator.Simulate(this.gameObject , vec); _rigidbody.isKinematic = false; _rigidbody.AddForce(vec, ForceMode.Impulse); } }
こんな感じでOK。
動かす
- 発射方向を変えるとそれに合わせて予測線も変わるか
- 発射する力を変えるとそれに合わせて予測線も変わるか
- massを変えると予測線も変わるか
- 予測線に沿って飛ぶか
この辺りに注目。
この次は
砲台を突然作り出したのはProbuIlderの勉強のため
今回でなんちゃって簡易砲台(作ろうとしているゲームでは砲台ではないんだけど)が出来上がったので、
次回からProBuilderを使用したステージ制作に入っていければいいな
【Unity】スライド操作で回転する砲台を作りたかったお話
何も考えずに砲台を作る
まずは図を観てほしい。
左右ドラッグで横回転、上下ドラッグで縦回転というシンプルなものだ。
で、発射ボタンか何かを押下するとforward方向へ発射されるという仕様と思って欲しい。
シンプルでいいんで、オブジェクトは弾だけで、自分で飛んでいくって感じ。
で、直感で作ったソースはこれ。テキトー。
Vector3 _touchDownPos; void Update(){ if (Input.GetMouseButtonDown(0)) { _touchDownPos = Input.mousePosition; } else if (Input.GetMouseButton(0)) { var tempPos = Input.mousePosition; Vector3 value = Vector3.zero; value.x = (_touchDownPos.x - tempPos.x); value.y = (_touchDownPos.y - tempPos.y); value.z = 0; _touchDownPos = tempPos; var qot1 = Quaternion.AngleAxis(value.x , new Vector3(0,1,0)); // 横回転 var qot2 = Quaternion.AngleAxis(value.y , new Vector3(1,0,0)); // 縦回転 this.transform.rotation *= qot1 * qot2;// Quartanion同士は掛け算で合体させる } // forwardチェック用のデバッグライン Debug.DrawLine(this.transform.position, this.transform.position + (this.transform.forward * 5)); }
動かしてみると
で、実際に動かすと、
- 左右スライド -> いい感じに回る
- 上下スライド -> いい感じに上下に向いてくれる
「完成だね!」
とは行かず、上下を角度を付けた状態で
左右に回すと斜めに回ってしまう。(当たり前だけど)
やはり自然に回ってほしい
数学的には上の動きで全く問題ないのだけど、
こう回るのが直感的でゲーム的な理想だと思う。
解決法(暫定)
とりあえず土台でやってしまった。
- 左右スライド -> 土台を回す
- 上下スライド -> 弾の角度を調整
var qot1 = Quaternion.AngleAxis(value.x , new Vector3(0,1,0)); var qot2 = Quaternion.AngleAxis(value.y , new Vector3(1,0,0)); _shooterBase.transform.rotation *= qot1; // 土台を回す this.transform.rotation *= qot2; // 砲塔を回す
というオブジェクトの分離で理想の動きになりました。
打ち終わったらまた弾に元に戻って欲しかったり、
発射位置がわからないと困ることも多いし…と土台があるとそれはそれで便利。
そもそも砲台だって土台と砲塔とって別れとるやんけ!
そう作ればええんや!
ってだけなんですが、計算で上手く回転軸を取ったり出来る…?
【Unity】【uGUI】 滑らかにスクロールするチャットウィンドウのようなものを作りたかった話
お疲れ様です。ぐーるです。
いきなりですけど、表題のようなものが作りたくて四苦八苦してました。
結局自作に終わったんですが、ちょっと経緯をメモっておこうかと。
ScrollRectで余裕っしょ
UnityにはScrollRectがあって、それはnormalizedPositionっていう値を持っていて、
その値でスクロール位置を制御できます。
こちらを利用しようとまず考えました。
追加する行について
今回作ろうとしていたのはチャットウィンドウなんで、文字の長さや改行によって
追加される行の高さはまちまちな訳です。
これはLayoutElementと自分計算で行けるなということで
_messageText.text = strMessages[Random.Range(0, strMessages.Length)]; // 適当なメッセージ _messageText.rectTransform.sizeDelta = new Vector2(_messageText.rectTransform.sizeDelta.x, _messageText.preferredHeight); _backGroundImage.GetComponent<RectTransform>().sizeDelta = new Vector2(_backGroundImage.GetComponent<RectTransform>().sizeDelta.x, _messageText.preferredHeight + 40);
こんな感じで、テキストの高さから計算して設定すればとりあえずいいでしょう。
スクロールイメージ
最初に起こしたイメージは
というイメージでAddする度に0.0fへ
毎回スクロールさせてあげればいいじゃん。シンプル。
実際これは問題無く実装出来ました。
void Update () { if (isScroll) { var val = _scrollRect.verticalNormalizedPosition; if(val > 0){ val -= scrollSpeed * Time.deltaTime; _scrollRect.verticalNormalizedPosition = val; } else { _scrollRect.verticalNormalizedPosition = 0; isScroll = false; } } } void AddRow() { var go = Instantiate(_rowPrefab); go.transform.SetParent(_tableContent.transform); isScroll = true; }
工夫すると
チャットシステムとはいえ、単純に作るとInstantiateの嵐になってしまいます。
なので、出来るだけコンポーネントを使い回したいなと考えました。
このようになると単純なスクロールでは上手くいかず、
少し計算が必要になりました。
float contentHeight = _tableContent.GetComponent<RectTransform>().sizeDelta.y; // 全体の長さ float beforeTextHeight = row.GetComponent<RectTransform>().sizeDelta.y; // 前の行の高さ float afterTextHeight = row.Create(); // Createの中では、ランダムにメッセージを生成している。行の高さを返却 float hDelta = contentHeight - beforeTextHeight + afterTextHeight; // 前回のを消し、新しい高さを計算 float normal = afterTextHeight / hDelta; // 最終的なスクロール位置 Debug.Log("contentHeight == " + contentHeight + " | before " + beforeTextHeight + " | after " + afterTextHeight + " | normal " + normal + " | delta " + (hDelta)); row.transform.SetAsLastSibling(); // 最下部にスライド
こんな感じで計算すると追加した行の手前に丁度フォーカスが動きます。
後はそのまま0.0fへ向かってスクロールすればOKです。
スクロールはいい感じです。
が、全体としてはどうでしょうか。
ScrollRectの再計算なのかガタっと表示が動く事があるため、微妙に荒い感じに…。
(gifで見ると悪くなく見えますが、実際見るとちょっとガタつきがあることがあります)
結局自作?
使い回しをせず、素直にInstantiateすれば問題ないです。
自動スクロールさせない、なんていうのも良いし、
アニメーションさせず、0.0fへ飛ぶような感じでも誤魔化せそう。
ちょっとヘンな事をしようとすると上手く行かなかった…って話でした。
実装でカバー出来そうだけど、自作のほうが制御が楽?
【Unity】C#初心者だけどUniRXを使えるようになりたい
uniRXいいですよね。
バリバリなUnity Developerって感じで。
とはいえ普段C#使わないレベルの初心者だと入りづらいのも事実で、
「そもそも使うメリットとかあるの?」
「余計わかりにくくなるだけじゃないの?」
なんて思ってたりもしました。
ちょっとググってみて、
「なんかチェーンでいい感じにスッキリ書けるみたい。」
「イベント駆動で処理を飛ばしたりと自由自在らしい。」
「MessageBrockerとかBufferとか便利みたいだけどどこで使えばいいのかよくわからん。」
なんて事も分かりました。
ちょっとお硬めなメリットとかはググった先の先人達の記事を参考にして頂くとして、
初心者の自分がどうやって使い始めたかだけメモとして書いてみようと思いました。
UpdateをUniRXで置き換えよう
void Update(){
}
unityでMonoBehaviorやってたら知ってますよね。
毎フレーム処理したい時に使うアレです。
これに処理を入れると
void Update(){ if(_isFire){ // 火を発生させる処理 _isFire = false; // 終わったらフラグを下ろす } // その他処理 }
こんなん書くことがあると思います。
これを言葉で説明してみると
「ファイアフラグが立っている時だけUpdateで火の処理をしたい」
という感じになります。
おお。そうか。これをUniRXに置き換えてみると
using UniRX; using UniRX.Triggers; void Start(){ this.UpdateAsObserbable().Where(_ => _isFire).Subscribe(_ => { // 火を発生させる処理 _isFire = false; // 終わったらフラグを下ろす }); } void Update(){ // もういらない // if(_isFire){ // // 火を発生させる処理 // _isFire = false; // 終わったらフラグを下ろす // } // その他処理 }
このように置き換えることが出来る。
とにかく void Update()は大きくなりやすい。
switchでstate切り分けたり、パラメータチェックしたり。
そのため、細かくメソッドに分けたり、処理を委譲させたりして
管理しやすくする涙ぐましい努力をしているのだけど、
その辺りを助けてくれるってわけだ。
開始アニメーション処理している時は…
this.UpdateAsObserbable().Where(_ => _isStartAnimation).Subscribe(_ => { // アニメーションさせるとか _isStartAnimation = false; // 終わったらフラグを下ろす });
敵が残っている時は…
this.UpdateAsObserbable().Where(_ => _enemies.Count > 0).Subscribe(_ => { // 敵が残っている時 });
HPが0になった時は…
this.UpdateAsObserbable().Where(_ => _player._hp < 1).Subscribe(_ => { // HPが0以下になったら });
とupdate処理をブロックごとに分けて書く事が出来る。
もちろん好き好きでは良いはずだ。
毎フレーム処理をしたいならvoid Update()に処理を書くのが最もシンプルで分かりやすい。
UniRXを使うとこう書けますよというだけだ。
ボタンイベントをUniRX化する
uGUI使ってたらButtonを使うこともあるはず。
普段なら
public void OnClickButton(){ // ボタン押されたら }
こんなん作って、Inspectorで紐付けるはずだ。
これもUniRXで置き換えて紐付けをやめてみよう。
[SerializableField] private Button _joinButton: _joinButton.OnClickAsObservable().Subscribe(_ =>{ // ボタン押されたら });
うーんスッキリ。だけどボタンだけは紐付けないとダメか。(GetComponent?)
Zenjectのidまで連携させればボタンの紐付けすらいらなくなるケド。
[Inject(id="join_button")] private Button _joinButton:
これはちと微妙かも?
(Buttonみたいな汎用なクラスをInjectするのはやめておいたほうが良さそう)
勉強中
まだ使い始めたばかりなので、これから少しずつ使って慣れていきたい所。
次はイベントの送出を勉強したいな。
(GameManagerみたいな親がいて、EnemyControllerのような子クラスのイベントをフックする、的なやつ)
以下コードイメージ。イメージだよ!合ってませんよ!
// 敵が倒されたらその敵に紐付いているスコアを加算して表示する _enemyController.EnemyDestroy(_ => enemy).Subscribe(e => { score += e.score; scoreText.text = "score : " + score; });
こういうイメージ…今まではdelegateとか使ってたけどUniRXならこんな感じで書けるのでは…!?
ととりあえずここまで。出来たらまたメモしよう。
【Unity】第7回 unity1week「当てる」参加作品「SATELLITE ONE」の開発
お疲れ様です。ぐーるです。
体調最悪のコンディションでしたが、なんとか提出することが出来ました。
公開されたので再掲。ワンショットシューティング、「SATELLITE ONE」よろしくお願いします! #unity1week https://t.co/tmStl0FFAC
— ぐーるさん (@uuha_goul) 2018年2月25日
思い出と実装をメモっておきたいと思います。
お題について
「当てる」かー、今回は神題だなと。
前提を考えなければアイデアはいくらでも出せそうだ、と。
デザインをほとんど必要とせずサクッと出来る、シンプルなものがいいなぁとか漠然と考えていて
降ってきたアイデアがSATELLITE ONEです。
内部設計について
いつもそうだけど物理的なunity.sceneは一つだけ。
内部的な画面は
- OpeningScene
- GameScene
- ResultScene
の3つ。
各画面間の切り替えはシンプルなステートマシンで管理。
public interface IState{ void OnStateEnter(); void OnStateExit(); } public class StateMachine : MonoBehaviour { public List<IState> states = new List<IState>(); private IState prevState; private IState currState; public void AddState(IState state) { foreach (var st in states) { if (st == state) { return; } } states.Add(state); } public void RemoveState(IState state) { states.Remove(state); } public void ChangeState<T>() where T : IState { if (currState is T) { return; } if (currState != null) { prevState = currState; prevState.OnStateExit(); // 抜ける前にOnStateExit()を呼ぶ } foreach (var st in states) { if (st is T) { currState = st; currState.OnStateEnter(); // 切り替わると同時にOnStateEnter()を呼ぶ } } } }
/** * // Zenjectで紐付け * [Inject] * private MainScene _mainScene; * [Inject] * private StateMachine _stateMachine; * // ~~ * _stateMachine.AddScene(_mainScene); // 起動時にStateMachineに登録しておく * // ~~ * _stateMachine.ChangeScene<MainScene>(); // _mainScene画面へ切り替え */ public class MainScene : MonoBehavior , IState{ //~~~~~ }
こんな感じでZenjectと組み合わせ。
楽か?って言われるとこの規模だとそこまででも無いけど
Zenject化するのが癖になってしまった。
ターゲットサイトについて
これは円形のプレーンな板をBlenderで用意しておいて、
その上で円を描くShaderを動かしています。
Shader "Custom/Ring" { Properties{ _TargetX("Target X", float) = 0 _TargetY("Target Y", float) = 0 _TargetZ("Target Z", float) = 0 _Alpha("Alpha" , float) = 0 _Color("Color" , Color) = (0,0,0,0) _BG("BG" , Color) = (0,0,0,0) } SubShader{ Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" } // alphaに対応するのに必要 Blend SrcAlpha OneMinusSrcAlpha // alphaに対応するために必要 LOD 200 CGPROGRAM #pragma surface surf Standard alpha:fade #pragma target 3.0 float _TargetX; float _TargetY; float _TargetZ; float _Alpha; float4 _Color; float4 _BG; struct Input { float3 worldPos; }; void surf(Input IN, inout SurfaceOutputStandard o) { float dist = distance(fixed3(_TargetX, _TargetY, _TargetZ), IN.worldPos); float val = abs(sin(dist*3.0 - _Time * 100)); float alpha = _Alpha; if (val > 0.98 && alpha > 0){ o.Albedo = _Color; o.Alpha = alpha; } else { // ↓何でも良い //discard; o.Alpha = 0; //o.Albedo = _BG; //o.Alpha = _BG.a; } } ENDCG } FallBack "Diffuse" }
[SerializeField] private Material _ringMat; // Update is called once per frame void Update () { // 適当なinputのラッパーだと思って下さい。なんでもいいです。 if(_inputManager.isTouchDownOnly(0)){ RaycastHit hit; Ray ray = Camera.main.ScreenPointToRay(_inputManager.GetTouchDownPosition(0)); if (Physics.Raycast(ray, out hit)){ var p = hit.point; p.y += 0.1f; this.transform.position = p; // タッチしたポイントをShaderに渡して中心点を移動した位置にする _ringMat.SetFloat(Shader.PropertyToID("_TargetX") , p.x); _ringMat.SetFloat(Shader.PropertyToID("_TargetY"), p.y); _ringMat.SetFloat(Shader.PropertyToID("_TargetZ"), p.z); } } }
ステージ制作について
SATELLITE ONEのステージは以下の2つの要素から成る。
- フロア
- 敵
そして敵は以下の要素を持っている
- 移動ルートポイント
- ルートポイントごとの移動速度
- 移動イージング
回転とかは賑やかしなので割愛。
ステージ作りがとにかく面倒だなと。
フロアはしょうがないとして、敵の移動ルートを量産出来れば、
ステージがどんどん作れそうだなと考えた。
ステージ制作画面
別でステージ製作専用のエディタを用意した。
- 敵を選択して選択状態に
- 動かしたい所をクリック
- AddPointボタンを押下でルートポイントを追加
という流れでどんどんルートポイントを置いていくことが出来る。
Playで動きの確認、そしてSaveでcsv出力している。
loadももちろん完備。
ここまで必要なのか?と思ったが
結局ゲーム側でも必要な機能がほとんどだったのでついでで。
csvのロードについて
ステージを追加量産するために
Application.dataPathを利用してパスでファイルを読むようにしてたら
当たり前だけどWebGLに出力したら動かない。
(開発中は紐付けなくてもcsvファイルを新規追加読み込み可能なので便利だった)
FileInfo fi = new FileInfo(Application.dataPath + "/Resources/stage/data/" + stageId + ".csv"); var result = new List<string>(); StreamReader reader = new StreamReader(fi.OpenRead()); while (reader.Peek() > -1) { string line = reader.ReadLine(); result.Add(line); } return result;
Editor上だけ使って実ゲームではTextAsset化して泥臭く紐付けて使うことに…。
とほほ。
[SerializeField]
private TextAsset[] _stageDatas;
おまけ
人工衛星なんですが、これはUnity標準のプリミティブオブジェクトの組み合わせで作ってます。
最初は無かったんですが、余りに味気なさすぎたので急遽追加しました。
適当に置いているだけなのですが、ちょっとゲームらしくなってくれたかなと。
感想
後はBGMのON/OFF付けたり、Post Processing Stack付けて微妙な色合いを出したり。頭痛と闘いながら調整。
今回もどうにか参加できました。
何故インフルエンザなんてかかるのか!?
ちゃんと予防接種受けてるのに。
前回は可愛い感じだったので、今回は硬めに。
みなさんお疲れ様でしたー!
次は可愛めで行きたい!
【Unity】Androidビルドを転送すると INSTALL_PARSE_FAILED_MANIFEST_MALFORMED が出る【NCMB】
ゲームが出来た!実機で動かそう!
と思い、ビルドを行い、adbで実機に転送しようとした所
xxxxx.apk: 1 file pushed. 3.6 MB/s (47771508 bytes in 12.536s) pkg: /data/local/tmp/xxxxxxx.apk Failure [INSTALL_PARSE_FAILED_MANIFEST_MALFORMED]
というエラーが出てしまった。
「あー、アプリ名に.(ドット)が入ってるのがマズいのかな?もしくはパッケージ名辺りが悪いとか」
と当てずっぽうで修正に入ったら変にハマってしまったのでメモっておく。
原因と解決策
原因はNCMBのUnitySDKプラグインを入れていまして、その中にある
Plugins/Android/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="YOUR_PACKAGE_NAME" > <!-- Put your package name here. --> <uses-sdk android:minSdkVersion="14"/> <!-- [START gcm_permission] --> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- Put your package name here. --> <permission android:name="YOUR_PACKAGE_NAME.permission.C2D_MESSAGE" android:protectionLevel="signature" /> <!-- Put your package name here. --> <uses-permission android:name="YOUR_PACKAGE_NAME.permission.C2D_MESSAGE" /> <!-- [END gcm_permission] --> ~~~~~
この
YOUR_PACKAGE_NAME
これだった…。
これをそのままにしてビルドして入れようとしてたので、エラーになっていたのでした。
これを修正したらあっさりインストール出来ました。
まずググろう
ほんとに…分からなかったらまずググろう。反省。
無駄な三十分を過ごしたー!あー!