20201014のUnityに関する記事は8件です。

スロープのレベルデザイン

サンプルデータのインポート

スクリーンショット (101).png
1:Fserverから「1_Slope_2020.zip」をPCにコピーしてダブルクリックし、エクスプローラーが表示されたら「展開」をクリックする。

スクリーンショット (102).png
2:続いて「すべて展開」をクリックする。

スクリーンショット (103).png
3:Unity Hubを起動する。

スクリーンショット (104).png
5:Unity Hubで「リストに追加」をクリックし、展開して生成した「1_Slope_2020」フォルダを選択する。この時、フォルダを開いて図のように「Assets」「Project Settings」フォルダが表示されたら、「フォルダーの選択」をクリックする。

スクリーンショット (105-2).png
6:Unityのバージョンが「2019.4.1f1」になっているか確認する。もし違っていたら、メニューから正しいバージョンを選ぶ。

スクリーンショット (105).png
7:プロジェクト欄に「1_Slope_2020」が表示されたら、プロジェクトをクリックする。

スクリーンショット (112).png
8:データが読み込まれて、Unityエディタが起動する。

ワンポイントテクニック

スクリーンショット (106).png
※不要なプロジェクトは「・・・」(縦3つ)メニューを開き、「リストから削除」を選択すれば削除できる。なお、このときデータはストレージから削除されずに残る。

シーンファイルの実行から作業準備まで

スクリーンショット (113).png
9:プロジェクトエリアから「Tamakorogashi」フォルダを開く。

スクリーンショット (114).png
10:続いて「Scene」フォルダを開く。

スクリーンショット (115).png
11:続いて「Slope」アイコンをクリックする。このUnityロゴマークのアイコンをしたファイルがシーンファイルだ。

スクリーンショット (129).png
12:シーンビューにステージが表示され、Hierarchyにアセットがリスト化される。Hierarchyの一番上のオブジェクトが「Slope」になっている点に注意しよう。その後、再生ボタンをクリックすると、デモが始まる。

スクリーンショット (117).png
13:デモはボールが落下してバウンドし、ゴールに到達するとファンファーレが鳴るというものだ。デモ中に「RETURN」キーを押すとリスタートする。

スクリーンショット (120).png
14:ある程度デモの内容を確認したら、再び再生ボタンをクリックしてプログラムを終了させよう。その後、「File」→「Save as」を選択する。

スクリーンショット (124).png
15:新たに「Slope_Master」と入力して保存しよう。

スクリーンショット (125).png
16:プロジェクトエリアのAssets直下に、新たにシーンファイル「Slope_Master」が作成される。

スクリーンショット (127).png
17:「Slope_Master」をプロジェクトエリアで「Assets」→「Tamakorogashi」フォルダにドラッグ&ドロップで移動させる。

スクリーンショット (128).png
18:再び「Slope」アイコンをクリックし、Hierarchyの先頭オブジェクトを「Slope_Master」から「Slope」に変える。以後はシーンファイル「Slope」に対して修正を加えていく。

最初からやり直したい場合

※最初からやり直したい場合は「Slope_Master」をクリックすれば、元の状態に戻せる。その際は「Slope」アイコンを削除し、「Slope_Master」を「Slope」にリネームする。その後「Save as」で新しく「Slope_Master」を作成し、あらためて「Slope」をクリックして、「Slope」シーンファイル上で作業をしよう。
無題.png
注意点

※作業は必ず「Slope」シーンファイル上で行おう。でなければ、RETURNキーでリスタートできなくなる。また、随時「Save」(Ctrl+S)で上書き保存して、フリーズなどの事態に備えよう。

ステージのレベルデザイン

スクリーンショット (981).png
21:はじめにHierarchyで「BG_Space」を選び、Inspector欄のSprite RenderからVisibility Optionsを選んで、隠しメニューを開く。その後、Order in Layerの値を「-1」にしておこう。これで背景が一番奥になる。

スクリーンショット (129).png
20:また、HierarchyでInspectorのTransform→Spaceのパラメータを増減すると、背景を拡大・縮小できる。Positionのパラメータで場所の移動、Rotationのパタメータで角度を変えられる。

スクリーンショット (130).png
21:プロジェクトエリアでAssets→Tamakorogashi→Prefabsを選択し、プロジェクトエリアに表示される各種のPrefabをHierarchyエリアにドラッグ&ドロップすると、シーンビューに新しいオブジェクトを表示させられる。

スクリーンショット (131).png
22:なお、配置したばかりのPrefabは既存のオブジェクトと重なっていることが多い。

12.png
23:そのため、場所が不明なときはHierarchyエリアでオブジェクトを選択して、移動させよう。

注意
スクリーンショット (131).png
プロジェクトエリアは倉庫で、Hierarchyはシーンビューに配置されるオブジェクトの一覧表だ。シーンビューからオブジェクトを削除するときは、Hierarchy上で削除しよう。プロジェクトエリアのアイテムを削除すると、復活させられないので注意!

スクリーンショット (118).png
24:何度もプレイと修正を繰り返して(=イテレーションを繰り返して)楽しいステージを作ろう。

Prefabsの種類

Slope

スクリーンショット (135).png
Slopeはボールが上を伝って転がり落ちる斜面だ。Transformで大きさ・角度・位置を修正できる。

スクリーンショット (137).png
Slopeの大きさを変更したら、InspectorのBox Collider 2Dの値を修正し、slopeの大きさと緑色の枠の大きさをあわせておこう。この緑色の枠が、いわゆる「当たり判定」に相当する。

Rotate

スクリーンショット (157).png
Rotateは自動的にくるくる回転する板で、当たり判定を持っている。InspectorのAuto RotateにあるRotation Speedのパタメータで回転速度を変えられる。値が正なら右回り、負なら左回りになる。

Bound

スクリーンショット (139).png
Boundはボールが高く跳ね返るバネだ。反射係数はBoundのInspectorからBox Collider 2D内にある「Extra Options」を開く。その後、Material欄にある「Bound」アイコンをクリックする。

スクリーンショット (140).png
続いて「Bound」アイコンを選択する。このBoundアイコンはFisix Material 2Dとよび、オブジェクトの反射度合いを設定できる。

スクリーンショット (141).png
InspectorにBoundの反射係数が表示される。Frictionで摩擦係数、Buncinessで反射係数を、それぞれ変更できる。

Boundを増やす

スクリーンショット (148).png
プロジェクトエリアからBoundのprefabsをHierarchyにドラッグ&ドロップすると、シーンビュー上に新しいBoundが配置される。ただし、これだけでは音が鳴らないので、あらためて設定する必要がある。追加したBoundのInspectorから「Condition Collision」にある「Custom Action()」の「None(Object)」メニューをクリックしよう。

スクリーンショット (149).png
Select ObjectウィンドウのSceneメニューから「Sound」アイコンを選択する。

スクリーンショット (150).png
「No Fliction」メニューから「AudioSource」→「PlayOneShot(AudioClip)」を選択する。

スクリーンショット (151).png
プロジェクトエリアのAssets→Tamakorogashi→Audioを選択し、「Jump01」ファイルをドラッグ&ドロップして設定する。これでボールが跳ね返ると音が鳴るようになる。

Boundごとに反射係数を変える

スクリーンショット (144).png
複数のBound Prefabsで個別に反射計数を変更したい場合は、新たにPhysics Material 2Dを作成し、反射係数を変えたいPrefabsに設定すれば良い。まず、プロジェクトエリアでAssets→Tamakorogashi→Visualを選び、右クリック→Create→Physics Material 2Dを選択する。

スクリーンショット (145).png
新しいマテリアルに名前(Bound2など)をつけて、Bouncinessの値を調整する。

スクリーンショット (146).png
設定したいPrefabsを選択し、Box Collider 2DのExtra Optionsを開いて、Materal欄に新しく作ったPhysics Material 2Dをドラッグ&ドロップする。

Ball

Ballの色を変える

スクリーンショット (138).png
Ballの色を変えるのは簡単だ。HierarchyからBallのInspector→Sprite Renderer→Colorでカラーピッカーをクリックし、任意の色に変更すれば良い。

Ballの重さを変える

スクリーンショット (982).png
Ballの質量を変えるには、HierarchyからBallを選択し、InspectorからRigidbody 2Dでパラメータを変更すればいい。各パラメータの意味は下記の通り。
Mass 質量
Friction 摩擦
Angular Friction 角摩擦
Gravity 重力

Ballを増やす

スクリーンショット (159).png
Ballを増やすには、プロジェクトエリアからBallをHierarchyにドラッグ&ドロップする。そのままではシーンビューでオリジナルのBallと同じ場所に表示されるため、場所を移動させる。追加されたBallには独自のパラメータを設定できる。

Move Floor

スクリーンショット (152).png
Move Floorは一定ルートで移動する板。Insopectorの「Patrol」で速度や移動ルートを設定できる。Speedのパラメータで速度が変わり(マイナスにすると逆方向に移動する)、Stopsの座標を変えるとUターンの座標を変えられる。

スクリーンショット (154).png
InspectorのPatrolにある「+」ボタンをクリックし、新しい座標を設定すると、新しい巡回ルートを設定できる。3つ以上のポイントを設定するとUターンではなく、一定軌道を周回する。ただし、このままでは保存されないので、もうひと手間が必要だ。

スクリーンショット (156).png
同じように「+」ボタンをクリックして、もう一つルートを設定する。その上で「ー」ボタンをクリックしてルートを削除すると、一つ前のポイントが保存される。

Burn

スクリーンショット (987).png
BurnはBallが当たると反射して消える板。プロジェクトエリアからHierarchyにドラッグ&ドロップして、シーンビュー上で場所を移動させれば良い。ただし、このままではBallが当たっても音は鳴らない。そのため「Boundを増やす」と同じように、別途設定する必要がある。

スクリーンショット (988).png
まずCondition Collisionの「None(Object Menu)」をクリックして、Select Objectウィンドウを表示する。

スクリーンショット (989).png
次にSceneメニューをクリックし、None(Object)メニューをクリックして、「Sound」アイコンをクリックする。

スクリーンショット (990).png
次に「No Friction」メニューをクリックし、AudioSource→PlayOneShot(AudioClip)を選択する。

スクリーンショット (991).png
この時点でオリジナルのPrefabsに設定されているオーディオファイル「powerup04」が設定されている。別のオーディファイルに変更してもOKだ。実際にゲームを起動して、ボールが当たったら音が鳴るか確認しよう。

Particle

スクリーンショット (133).png
HierarchyのParticleを選択し、InspectorのBox Collider 2DのScaleの値を変更すると、星の瞬きの出現する範囲を変えられる。またObject Creator AreaのOther Option欄にあるSpawn Intervalの値を変えると、明滅の間隔を変えられる。なお、Particleはオブジェクトであって、Prefabesではない。FlagRedと同じく数を増やす必要が無いため、Prefabsには設定されていない。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Unity]Core Hapticsを使ってiOSでバイブレーションを自由に動作させる

単純なバイブレーションの実装

 Unityでは

Handheld.Vibrate ();

というメソッドが用意されていて、iOSでもAndroidでも振動させるだけならこれ一つで実現できます。
が、これは ただ1秒くらい「ブーン」となる以外に何も調整ができず 、UXや演出として利用するには流石に "わびさび" が足りません。

ちょっと凝ったバイブレーションの実装

 ちょっとだけ手間ですが、iOS、Androidのネイティブ処理を呼び出すことで少し融通の利くバイブレーションを実装できます。下記の記事が分かりやすくまとまっていて、参考になります!

 Androidは端末のバリエーションが多すぎる故か、これで 振動時間を調整するのが限界 ですが、一応Unityに用意されたメソッドを呼ぶよりはマシなバイブレーションが実装できるはずです。

 iOSでは、AudioServicesPlaySystemSound というネイティブのメソッドを使うことで、 OSに用意されたサウンドID を渡し、その中で音無し+バイブレーション有りのパターンのものを使うことができます。

 ざっと確認した感じ、使えそうなのは以下のサウンドIDでした。

1003:ブッ(ごく小さな音が入っているが、ほぼ聞こえず)
1011:ブッブー
1102:ププッ
1161:カリッ
1162:カリカリッ
1164:カリカリカリ...
1311:ブッブー
1350:ブーッ
1519:プッ
1521:プルッ

 通知やUX用に用意されたパターンの振動なので、それなりに使い勝手の良いバイブレーションが揃っています。 めちゃくちゃこだわるようなことがなければこの中から選べばよい と思います。
が、あくまでパターンの中から選ぶ形なので、振動時間は調整できないし、縛りが強いと言わざるをえません。

 しかし、 Haptics を使えばかなり細かい振動を実装できます。

Haptic Feedback(触覚フィードバック)

 iPhoneは、iPhone7以降、ホームボタンが物理ボタンではなくなりました。(2020 10/16 追記:嘘でした。バリバリ物理ボタンありましたw)
そこで生まれたのが 「Haptic Feedback」 と呼ばれる、 タッチディスプレイ上でもボタンを押したようなフィードバックを得られるバイブレーション の仕組みです。
iPhone7以降の端末では、画面の下から上にフリックすることでホーム画面へ戻ることができますが、その時に「グッ」というような振動が起きる、あれです。

 そして iOS13 以降、この仕組みを自由に使える Core Haptics というAPIが追加されました。
これを使えば、振動時間だけでなく、 振動の「強さ」や「鋭さ」 というような、Haptic Feedback で使われていたかなり細かなバイブレーションのパラメータを調整することができます(この辺はOSも端末も一社で作ってるAppleにしかできない仕組みですね)。
生まれた経緯ゆえ、 かなりUXの強化に向いた機能 になっています!

 ということで、本記事ではそれをUnityから使うためのアレコレをまとめます。
ネイティブ実装は普段頻繁にやるようなものでもないし、Objective-C や Swift を書くことになるので、思いのほか手こずりました(というか徹夜するハメになりました)。
この記事でこれから Haptics を使う人が徹夜しなくて済むようになりますように。。。

■余談 - Macbook の Haptics

 最近のMacbookではトラックパッドを押したときに「カチッ」という感触がありますが、実はあれも Haptic Feedback と同じ仕組みを使ってるらしいです。実際には押し込めるような構造になってないので電源を消してトラックパッドを押しても何にも感触がありません。(自分もHaptics を調べてるときに知ってクッソ驚きました)

検証環境

本記事は以下のバージョンで動作を確認してます。

  • Unity 2018.4.11f1
  • Xcode 11.1

ファイルの作成

 Core Haptics はiOSネイティブで提供されているAPIで、Objective-C でも Swift でも呼び出せるようになっていますが、今回は Swift での実装方法 を紹介します。

image.png

 上図のように、Unityから Swift の処理を呼ぶには iOSプラグインを経由する必要があります。
以下のフォルダ構造で、それぞれファイルを作成してください。

Assets
┣ VibrationUtil.cs
┗ Plugins
 ┣ VibrationObjc.mm
 ┗ iOS
   ┗ VibrationSwift.swift

■余談 - なんでSwift?

 Objective-C で処理を書けば、iOSプラグインファイルで直接 Core Haptics API が呼べます。
普段 Objective-C はおろか、Swift も書かない僕はその方法で済ませたかったですが、公式ドキュメントの実装例 が なぜか Swiftの分しか無かったので、今回は泣く泣くSwiftを選ぶことにしました。

SwiftでCore Hapticsの処理を記述

ファイル全文

VibrationSwift.swift
import Foundation
import CoreHaptics

@objc public class VibrationSwift : NSObject {
    @available(iOS 13.0, *)
    @objc public static var hapticEngine: CHHapticEngine?

    @available(iOS 13.0, *)
    @objc public static func setupHapticEngine() {
        do {
            hapticEngine = try CHHapticEngine()
            try hapticEngine?.start()
        } catch {
            print("Failed to restart the engine")
        }
    }

    @available(iOS 13.0, *)
    @objc public static func playHapticEngine(intensityValue: Float, sharpnessValue: Float, durationValue: Float, sustainedValue: Float) {
        if hapticEngine == nil {
            setupHapticEngine()
        }

        let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: intensityValue)
        let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: sharpnessValue)
        let sustained = CHHapticEventParameter(parameterID: .sustained, value: sustainedValue)

        let event = CHHapticEvent(eventType: .hapticContinuous, parameters: [intensity, sharpness, sustained], relativeTime: 0, duration: TimeInterval(durationValue))

        do {
            let hapticPattern = try CHHapticPattern(events: [event], parameters: [])
            let hapticPlayer = try hapticEngine?.makePlayer(with: hapticPattern)
            try hapticPlayer?.start(atTime: 0)
        } catch {
            print("Failed to play")
        }
    }
}

解説

import CoreHaptics

まず、必ず CoreHaptics をインポートしてください。
 

@objc public static var hapticEngine: CHHapticEngine?

CoreHaptics による振動を管理する CHHapticEngine の変数です。static にして、static メソッドから参照できるようにしてください。
 

@objc public static func setupHapticEngine()

変数定義した hapticEngine の初期化処理です。
 

@objc public static func playHapticEngine(intensityValue: Float, sharpnessValue: Float, durationValue: Float, sustainedValue: Float)

Hapticsによる振動メソッドです。引数はそれぞれ以下のパラメータを表しています。

  • intensityValue:振動の強さ(0~1)
  • sharpnessValue:振動の鋭さ(0~1)
  • durationValue:振動する時間(秒)
  • sustainedValue:指定した時間の間、振動させ続けるか否か(0ならfalse、1ならtrue)
    • durationValue の時間は、これがtrueでないと無効となります

メソッド内の CHHapticEventParameter を使えば、他にもいろんなパラメータを扱えるようですが、ひとまず主要なものはこんなところかなと。

■参考 - その他のパラメータ

  • attackTime:触覚パターンの強度が増加し始める時間
  • decayTime:触覚パターンの強度が減少し始める時間
  • releaseTime:触覚パターンのフェードを開始する時間

ドキュメント の「Haptic Event Parameter IDs」を参照

let attackTime= CHHapticEventParameter(parameterID: .attackTime, value: 0)

というようにパラメータを定義し、CHHapticEventparameters に渡すことで設定できます。
上記コードを例にすると、

let event = CHHapticEvent(eventType: .hapticContinuous, parameters: [intensity, sharpness, sustained, attackTime], relativeTime: 0, duration: TimeInterval(durationValue))

こんな感じです。(sustained の後に attackTime を追加)

■参考 - 遅延実行

CHHapticEventrelativeTime の引数を使うことで、遅延実行をすることもできます。
例えば「ブブッ」というような複数回の振動パターンを作るときに必要になるでしょう。

let event = CHHapticEvent(eventType: .hapticContinuous, parameters: [intensity, sharpness, sustained], relativeTime: TimeInterval(1), duration: TimeInterval(durationValue))

こんな感じで、TimeInterval をかまして時間(秒)を指定してください。
 

@available(iOS 13.0, *)

各変数/メソッドの前に書かれているこの記述ですが、これは Core Haptics が iOS13 から導入されたものであるため、それ以前のバージョンのOSからは呼ばないでね、というやつです。無いとエラーが出ます。

Swiftの処理をiOSプラグインで呼び出し

ファイル全文

VibrationObjc.mm
#import <Foundation/Foundation.h>
#import <CoreHaptics/CoreHaptics.h>
#import <UnityFramework-Swift.h>

#ifdef __cplusplus
extern "C" {
#endif
    void setupHapticEngine() {
        [VibrationSwift setupHapticEngine];
    }

    void playHapticEngine(float intensity, float sharpness, float duration, float sustained) {
        [VibrationSwift playHapticEngineWithIntensityValue:intensity sharpnessValue:sharpness durationValue:duration sustainedValue: sustained];
    }

#ifdef __cplusplus
}
#endif

解説

#import <UnityFramework-Swift.h>

 これは、Xcodeのビルド時に自動的に作られるSwiftのヘッダーファイルです。
何もしないとファイル名は 「{プロジェクト名}-Swift.h」 となるのですが、それだと汎用性に欠けるので、後ほどUnityの実装で任意の名前に固定します。※今回は 「UnityFramework-Swift.h」 としました
 

[VibrationSwift 〇〇〇];

VibrationSwift.swiftで定義した VibrationSwift クラスの、「〇〇〇」というメソッドを呼ぶ、という記述です。
 

[VibrationSwift playHapticEngineWithIntensityValue:intensity sharpnessValue:sharpness durationValue:duration sustainedValue: sustained];

 VibrationSwift クラスの「playHapticEngine」というメソッドを呼んでいる…のですが、ここ、Objective-C の独特な文法です。
第1引数を渡すとき、メソッド名に 「With+イニシャルを大文字にした引数名」 という記述をくっつけて、コロン(:)の後に第1引数書いています。第2引数以降は「引数名:引数」で書いていけばOKです。
Objective-Cを知らないが故、ちょっとハマりました。

iOSプラグインの処理をUnityで呼び出し

 ようやくUnityに辿り着いた!

ファイル全文

VibrationUtil.cs
using System.Runtime.InteropServices;
using UnityEditor.Callbacks;
using System.IO;
#if UNITY_IOS
using UnityEditor.iOS.Xcode;
#endif

public class VibrationUtil {
#if UNITY_IOS && !UNITY_EDITOR
    [DllImport("__Internal")]
    private static extern void setupHapticEngine();

    [DllImport("__Internal")]
    private static extern void playHapticEngine(float intensity, float sharpness, float duration, float sustained);
#endif

    public static void SetupHapticEngine() {
#if UNITY_IOS && !UNITY_EDITOR
        setupHapticEngine();
#endif
    }

    public static void PlayHapticEngine(float intensity, float sharpness, float duration) {
#if UNITY_IOS && !UNITY_EDITOR
        float sustained = duration > 0 ? 1 : 0;
        playHapticEngine(intensity, sharpness, duration, sustained);
#endif
    }

    [PostProcessBuild(0)]
    public static void OnPostprocessBuild(BuildTarget target, string path) {
        string projectPath = PBXProject.GetPBXProjectPath(path);
        PBXProject pbxProject = new PBXProject();
        pbxProject.ReadFromString(File.ReadAllText(projectPath));
        string target = pbxProject.TargetGuidByName("Unity-iPhone");

        pbxProject.SetBuildProperty(target, "SWIFT_VERSION", "4.2");
        pbxProject.SetBuildProperty(target, "SWIFT_OBJC_INTERFACE_HEADER_NAME", "UnityFramework-Swift.h");
        pbxProject.AddFrameworkToProject(target, "CoreHaptics.framework", false);

        File.WriteAllText(projectPath, pbxProject.WriteToString());
    }
}

解説

[DllImport("__Internal")]
private static extern void 〇〇〇();

iOSプラグインで定義した「〇〇〇」というメソッドをC#(Unity)に定義する、という記述です。
定義すれば、C#内で普通のメソッドのように扱えます。
 

[PostProcessBuild(0)]
public static void OnPostprocessBuild(BuildTarget target, string path)

PostProcessBuild を付けたメソッドは、UnityのiOSビルドでXcodeプロジェクトが出力された後に呼び出されるようになります。(カッコ内の数字はその順番。小さいほど先に呼ばれる)※ドキュメント
これを利用して、Xcodeプロジェクトでやるべき設定を自動化しています。
 

pbxProject.SetBuildProperty(target, "SWIFT_VERSION", "4.2");

Xcodeで使うSwiftバージョンを指定します。これが無いと、「Swiftバージョンが未指定」という旨のエラーが出るはずです。
"4.2"の部分は、 使用しているXcodeがサポートしてるSwiftバージョンを使ってください
 

pbxProject.SetBuildProperty(target, "SWIFT_OBJC_INTERFACE_HEADER_NAME", "UnityFramework-Swift.h");

「Swiftの処理をiOSプラグインで呼び出し」の節(VibrationObjc.mm)で説明した、Swiftのヘッダーファイルの名前を指定しています。VibrationObjc.mm で記述するヘッダー名と一致させれば、任意の名前を付けることができます。
 

pbxProject.AddFrameworkToProject(target, "CoreHaptics.framework", false);

CoreHapticsのフレームワークをXcodeに追加しています。

使用例

VibrationTest.cs
using UnityEngine;

public class VibrationTest : MonoBehaviour {
    void Start() {
        // Hapticsの初期化
        VibrationUtil.SetupHapticEngine();
    }

    void Update() {
        // 画面タッチした瞬間を検出
        if (Input.GetTouch(0).phase == TouchPhase.Began) {
            VibrationUtil.PlayHapticEngine(1f, 1f, 0.1f);
        }
    }
}

このコンポーネントをシーン上の適当なゲームオブジェクトにアタッチすれば、画面をタッチするたびにバイブレーションが発生します。簡単!

補足

 その他、いろいろ実装に当たって必要だったことや気づいたことです。

■Haptics 対応端末かどうかの判定

APIにはそれらしいメソッドが見つからなかったので 「iPhone8以降、iOS13以上」 というCore Haptics の動作条件に従って、泥臭い方法で判定メソッド作ってみました。

VibrationUtil.cs
using UnityEngine;

~~~中略~~~

public static bool IsSupportedHapticEngine() {
#if UNITY_ANDROID || UNITY_EDITOR
    return false;
#endif
    string versionString = SystemInfo.operatingSystem;
    versionString = versionString.Replace("iOS ", "");
    float version = -1f;
    float.TryParse(versionString.Substring(0, 2), out version);
    if (version < 13) {
        // iOS13未満は未対応
        return false;
    }

    string deviceString = SystemInfo.deviceModel;
    if (!deviceString.Contains("iPhone")) {
        // iPhone以外の端末(iPadなど)は未対応
        return false;
    }

    deviceString = deviceString.Replace("iPhone", "");
    float deviceID = -1f;
    float.TryParse(deviceString.Substring(0, 2), out deviceID);
    // デバイスモデル名が「iPhone10.0」以上(=iPhone8以降の機種)
    // 参照:http://www.enterpriseios.com/wiki/iOS_Devices
    IsSupportedHaptics = deviceID >= 10;

    return IsSupportedHaptics;
}

この方法だと、新型のiPhoneやiPadが出るたびにHaptics対応してるか確認しないといけないので困った…。公式のAPIで判定できるのを待つしかないのかなぁ。

▼絶対にマネしてはいけない方法

VibrationObjc.mm
#import <UIKit/UIKit.h>
VibrationObjc.mm
bool isSupportedHaptics() {
    NSNumber *supportLevel = [[UIDevice currentDevice] valueForKey:@"_feedbackSupportLevel"];
    return [supportLevel intValue] == 2;
}

stack overflowのトピック で見かけた方法なのですが、プライベート変数でそれらしい数値を取ることができるらしく、「2 以外のときは Haptic 未対応端末」と判定できるみたいです。
が! 非公開APIを触るのはバイナリを解析すれば分かるので、余裕でAppStoreからリジェクトくらいます。 やめようね!

余談ですが、この指摘をくださった方曰く…

以前、実装の名前がたまたま非公開APIと完全一致しただけでもリジェクトされました T-T

恐ろしスギィ!

■アプリをバッググラウンドに入れたり端末をスリープさせると振動しなくなる

VibrationSwift.swift で定義してる hapticEngine が破棄されてる?

VibrationTest.cs
private void OnApplicationPause(bool pauseStatus) {
    if (!pauseStatus) {
        // アプリ復帰時、HapticEngine初期化
        VibrationUtil.SetupHapticEngine();
    }
}

ひとまず上記のように、アプリが復帰するたびに初期化処理を呼べば再現しなくなりましたが、リークしてそうな気がして怖い。どうなんでしょう。

■Haptics でUXデザインするときのコツ(公式ガイドライン)

Human Interface Guidelines - Haptics の、 Designing with Haptics の部分に「こういうことを意識してね」「こういうことに気を付けてね」という指標が書いてあったので、ざっくり要約してみました。

  • 「何で今振動したの?」という体験がないようにすること
  • 単品ではなく、目に映る演出や音とセットで振動させること
  • むやみやたらに振動させてユーザーにストレスを与えないようにすること
    • 振動が苦手な人も「このくらいならOFFにしなくてもいいか」くらいがGood
  • 同じシチュエーションでは同じ振動をさせること(一貫性)
    • なんでここでは振動しないの?はNG
  • できるだけいろんな実機×人間で試すこと(ストレスに感じるかどうか個体差が凄い)
  • オプションでOFFにできるようにすること
  • 操作量やレスポンスの把握しやすさを向上させる使い方を意識すること
    • 最大スワイプに達したとき、UIをタッチできたときなど、判断スピード向上できるのが望ましい
  • カメラやマイク、ジャイロの妨げにならないように気を付けること

たぶんこういう旨のことが書いてました。いずれも納得の内容。
iPhoneのシステム標準で使われてる Haptics Feedback は凄くナチュラルで小気味良いので、ここに辿り着くまでにAppleでたくさんの試行錯誤があったんだろうなぁ…。その結果が知見となってこのガイドラインを生んだと思うので、まずは従ってみたほうが良いでしょう。
タッチディスプレイとの組み合わせでデザインするUX なので、コンシューマーゲームのバイブレーションとはまた違う考え方をしないといけないですね。

■OnPostprocessBuildで記述してるXcodeの設定箇所

CoreHaptics.framework がなぜか一度Xcodeで直接追加しないとスクリプトによる追加が働かないっぽい挙動にハマったことがあったので、一応直接設定する場所も記載しておきます。(たぶんスクリプトで大丈夫ですが…念のため…)

▼CoreHaptics.framework

image.png
 

▼「SWIFT_OBJC_INTERFACE_HEADER_NAME」、「SWIFT_VERSION」

image.png

Swiftのバージョンは、ここで選択できる数字をスクリプトで指定すればよいかと

参考記事

▼Core Haptics

▼Unity - iOSプラグイン - Swift 連携

▼その他

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Universal Render Pipelineでパーティクルが表示されない場合の対処法

PR: CADDiではバックエンドエンジニア、フロントエンジニア、アルゴリズムエンジニア、SRE等などを募集しています

UnityでUniversal Render Pipelineを使っている場合、パーティクルが表示されなくなる場合がある。

この時、そのパーティクルのマテリアルがSoft Particlesを有効化している場合、Scriptable Render Pipeline SettingsのDepth Textureを有効化することで問題が解決する場合がある。

これは、Soft Particlesを利用するためには深度テクスチャが必要だが、デフォルトではそれが無効になっているために発生する問題である。

尚、今後のアップデートで警告を表示する計画があるようだが、Unity 2019.4.12f1、Universal RP 7.3.1ではまだ実装されていない。

I will make a PR, hopefully next week, that addresses this issue. Basically soft particles need the depth texture in order to work correctly but we need to warn people when Depth Texture turned off in the Asset plus also prevent particle disappearing in builds.

以下の場合、問題が顕在化しなかったり、再現する条件が分かりづらくなるので注意する。

  • ゲームビューとシーンビューを同時に表示している場合、問題が顕在化しない。
  • ゲームビューをFree Aspectにしている場合、プレイ開始後に一度シーンビューをアクティブにしてからゲームビューに戻ると問題が潜在化してしまう。
  • ゲームビューをFree Aspect以外にしている場合、シーンビューではパーティクルが表示されるが、ゲームビューでは表示されない。1

  1. シーンビューを表示してからゲームビューを表示すると、再描画されるまでは表示されたりする。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity製の新アプリ「ポイント計算機」がリリースしました!

app_point_calc_1.png

この度はUnity製の新アプリ「ポイント計算機」がリリースしました!
併せて「ポイ活の始め方」と「アプリの使い方」記事を書いてみたのでよろしければ
ぜひこちらも読んでみてください!

・ダウンロード
https://is.gd/sSrnyY

・ポイ活の始め方
https://is.gd/3p46Vu

・アプリの使い方
https://is.gd/Fd57L7

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】Fungusの選択結果をLuaで取得しC#に渡す方法

前回の記事の続きになります。

LuaからC#の関数、変数を呼び出し方を説明します。

LuaでUnity上の操作を行えるようになるので格段に表現方法が増えます。

前提条件としてHierarchyに

  • Flowchart
  • LuaEnvironment
  • LuaScript
  • LuaBindings

の4つの配置が必要です。

ステップ1 値の受け渡しをするGameObjectを追加する

Unityで値を受け渡しするための、GameObjectを追加します。

ここでは仮に「MainManager」とします。
Hirerarchy上で空のGameObjectを追加し、名前を「MainManager」にしてください。
同名のC#スクリプトをProjectに追加し、Hirerarchy上の「MainManager」にアタッチします。

MainManagerに以下のスクリプトを記述してください。

using Fungus;
using UnityEngine;

public class MainManager : MonoBehaviour
{
    // Hirerarchy上に配置したLuaScriptをアタッチしておく。
    [SerializeField] private LuaScript _luaScript;

    // 選択肢の結果を格納する変数。
    public int Result { get; set; } = 0;

    // Luaスクリプトを実行し、Fungusを表示するメソッド
    public void OnFungusStart()
    {
        // Luaスクリプト実行前。初期値の「0」が表示される。
        Debug.Log($"result value 1 : {Result}");

        // Luaスクリプトを実行し、Fungusの会話、選択肢を表示する。
        _luaScript.OnExecute();

        // Luaスクリプトは非同期で実行されるため、初期値の「0」が表示される。
        Debug.Log($"result value 2 : {Result}");
    }

    // Fungusの終了を通知するコールバックメソッド
    public void OnCallback()
    {
        // メソッド「OnFungusStart」の実行後に、このメソッドを実行すると、Fungusで選択した結果が表示される。
        Debug.Log($"result value 3 : {Result}");
    }
}

ステップ2 LuaBindingsのObjectBindingsに新規行を追加する

LuaBindingsのObjectBindingsの「+」ボタンをクリックし、新しい行を追加します。

1.png

2.png

ステップ3 MainManagerをObjectBindingsのObjectにアタッチする

ステップ2で追加したObjectBindingsのObjectにMainManagerをドラッグ&ドロップします。

3.png

ステップ4 ObjectBindingsのComponentのリストからMainManagerを選択する

ステップ3のタイミングでComponentのリストにMainManagerが追加されるので、選択します。

4.png

ステップ5 LuaからC#の関数、変数を呼び出し方を検索する

ステップ3のタイミングでMenberInfoにmainmanagerの項目が追加されます。
mainmanagerを選択するとpublicな関数、変数が表示されます。
変数は自動的にC#側の値を取得する「get_変数」関数、C#側に値を渡す「set_変数」関数が作成されます。

今回は試しに「set_Result(value)」を選択します。

5.png

LuaUsageの項目が表示され、Luaでの使用するためのスクリプトが提示されます。

6.png

ステップ6 Luaスクリプトの作成

Luaスクリプトを記述します。

Luaスクリプトの作り方は前々回の記事を参考にしてください。

-- C#側から値を取得する
local result = mainmanager.get_Result()

say("テスト選択肢を表示します。"..result)

-- 選択肢を表示する
local choice = choose{ "左", "右" }

if choice == 1 then
    say("左を選択しましたね。")
elseif choice == 2 then
    say("右を選択しましたね。")
end

-- 選択肢の結果を変数に格納する。選択肢の結果は左から1で始まる。
result = choice

say("テスト選択結果を表示します。"..result)

-- 選択肢の結果をC#側に通知する
mainmanager.set_Result(result)

-- Fungus側の処理が終わったことをUnityに通知する。
mainmanager.OnCallback()
local result = mainmanager.get_Result()

でC#側からLua側に値を引き渡しています。

mainmanager.set_Result(result)

でLua側からC#側に値を引き渡しています。

ステップ1のC#側の関数「OnFungusStart」を実行するとLuaを仲介しFungusにC#側の値を引き渡していることが確認できます。

注意点としましては、Lua側は非同期で実行されるため、呼び出し結果はコールバック関数を用意し、Lua側で呼び出す必要があります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】個人的におすすめAsset

初めに

この記事は私が触ってみたAssetなどを無料、有料問わず紹介していきます。

一覧

無料

3D Game Kit

3D Game Kit
3Dアクションゲームを作成するのであればまずこのAssetを一度importして試してほしい!
このAssetには3Dのアクションゲーム(10分程度で終わる)が入っています、どんなふうに3Dのアクションゲームを作ればいいのかわからない人は大勢いると思います、このimportしたプロジェクトではどんな設定になっているのかが一目瞭然なのでゲーム作るための参考になりました。
9b01fb8d-6fd7-4bf9-80f6-f383c19e9aa8.png

2D Game Kit

2D Game Kit
2Dアクションゲームを作るにあたってどのような設定をしているかやどんなプログラムを組んでいるか2DGameを作るうえで必要なものが詰まっています、こちらのAssetを参考に2DGameを作れればかなりの質が保証されるでしょう。
8b5d740f-32ee-434f-85ba-99d5a7432970.png

Lost Crypt - 2D Sample Project

Lost Crypt - 2D Sample Project
Unityで2DGameで使える2Dツールが盛りだくさんなサンプルプロジェクトです、URP(Universal Renderer Pipeline)を使用したプロジェクトで2Dlightなどを使用し2DGameで見た目の質を高めるためのものが盛りだくさんなAssetです。
5bbedf97-1d91-4588-9e3f-dfefd0d8375f.png

Cartoon FX Free

Cartoon FX Free
toon調のエフェクトです2Dや3D両方のエフェクトが搭載されています、エフェクトってどんな設定で作られているか気になった方に是非見てほしいものです。
873f90fd-5628-4239-ac11-41fb48497faf.png

有料

Easy Mobile Pro

Easy Mobile Pro
広告、アプリ内購入、ゲームサービス、通知、共有など、すべてのモバイルゲームに共通する機能の実装を大幅に楽にしてくれるAssetです、モバイルのゲームを売りに出したい方!めんどくさい広告の設定など全部このツールに任せてゲームを面白くすることだけを追求できます!
b22561fd-cb7e-4de0-90c3-92697616e6ab.png

All In 1 Sprite Shader

All In 1 Sprite Shader
Spriteに様々な効果を与えるShaderが多数詰まったAssetです!燃える表現からノイズが走ったり光ったりSpriteに様々なエフェクトを付与するこができます!Shaderの勉強にも使えるかも?
1412506a-dbb6-4dc3-803e-ef0ef79b4509.png

POLYGONシリーズ

POLYGONシリーズ
ローポリのステージから人、モンスターまで様々な環境のローポリ素材を販売しているシリーズです。
2e19d792-aaa8-4143-b999-ce419ccc11ab.png

Corgi Engine - 2D + 2.5D Platformer

Corgi Engine - 2D + 2.5D Platformer
2D,2.5Dのゲームを作るために最適化されたエンジンです、プレイヤーの移動から壁ジャンプ、2弾ジャンプ、2DGameに欲しい要素がたくさん含まれています。このAssetで使われているソースを見て勉強すれば大抵のゲームは作れるんじゃないかな?と思えるほどの情報量でした。
5a2a49a9-5190-4ebe-83de-efe34bb37ec1.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unityの条件分岐色々※随時更新

ゲームオブジェクトがアクティブかどうか

GameObject.activeSelf を使う。
activeSelf は、ゲームオブジェクトのアクティブ状態を読み取る専用のメソッド。
参考: GameObject-activeSelf - Unity スクリプトリファレンス

if(A.activeSelf == true)
{
  Debug.Log("Aがアクティブな場合表示される");
}

アクティブ状態に関するメソッドに、SetActive が存在するが、こちらはアクティブ状態の切り替えに使用されるため、アクティブ状態を利用した条件分岐には使えない。

// NG例
if(A.SetActive == true)
{
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity開発環境をdocker上に構築する

概要

  • ホスト側のGUIでdocker上のUnityEditorを起動する方法を紹介します
  • UbuntuのUnity導入手順が面倒だったのでdockerfileにまとめました
  • ほとんど前回の投稿と同じ内容です
  • 簡単に使える dockerfile がなかったので作って公開しました → chikuta-dockerfiles

検証環境

今回検証した環境は以下になります。

Hardware
cpu - AMD Ryzen 9 3900X
gpu - Geforce RTX 2060 super
ram - 32G

Software
os - ubuntu 20.04.1
kernel - Linux define 5.4.0-48-generic
docker - 19.03.13
nvidia-driver - 450.66
cuda version - 11.0

nvidia-smi の出力を添付しておきます。

$ nvidia-smi
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 450.66       Driver Version: 450.66       CUDA Version: 11.0     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  GeForce RTX 206...  Off  | 00000000:0E:00.0  On |                  N/A |
| 38%   40C    P8    11W / 175W |    916MiB /  7979MiB |      1%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

環境整備

最初にnvidia-container-runtimeをインストールします。

# apt 関連設定
$ curl -s -L https://nvidia.github.io/nvidia-container-runtime/gpgkey | sudo apt-key add -
$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
$ curl -s -L https://nvidia.github.io/nvidia-container-runtime/$distribution/nvidia-container-runtime.list | sudo tee /etc/apt/sources.list.d/nvidia-container-runtime.list

# nvidia-container-runtimeインストール
$ sudo apt update
$ sudo apt install nvidia-container-runtime

# docker service の再起動
$ sudo systemctl restart docker.service

次に docker group に自身の UID を登録します。docker を使っている人はやっていると思いますが念の為。

$ sudo usermod -a -G docker `id -un`
$ sudo systemctl restart docker.service

実行方法

次にdockerイメージのビルドを行います。

$ git clone https://github.com/chikuta/chikuta-dockerfiles.git
$ cd chikuta-dockerfiles
$ ./docker/ros-bionic-melodic-unity/build-docker-image.bash

次に以下のコマンドを実行することでビルドしたイメージのコンテナを起動できます。スクリプトの中身はxhost周りの処理とdockerの引数をラッピングしたものです。必要に応じて編集をすれば自分の環境を作れるで積極的にカスタマイズしましょう。

$ cd chikuta-dockerfiles
$ ./docker/ros-bionic-melodic-unity/run-docker-container.bash

スクリプトを実行すると青色のterminatorが立ち上がります。これは dockerコンテナ上で動いているターミナルアプリケーションになるため、ここで実行したコマンドは全てdockerコンテナ上で処理されます。
次にUnityHubを立ち上げてみます。

cd /opt/unity
./UnityHub.AppImage

unityhub.png

必要な環境をインストールした後にUnityEditorを立ち上げてみましょう。
画像は適当な Free Island Collection をAsset Storeから取得して表示したものになります。

unityeditor.png

次はROS連携あたりを書いていきます。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む