[ちょっとニッチ] Unity3dでiOS/Android端末の画面輝度を取得/変更(PlayMaker用Action)

# iOSネイティブプラグイン準備

Plugins/iOS以下に配置

```objc:BrightnessPlugin.m
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

#ifdef __cplusplus
extern "C" {
#endif

void setBrightness(float val) { [UIScreen mainScreen].brightness = val; }

float getBrightness() { return [UIScreen mainScreen].brightness; }

#ifdef __cplusplus
}
#endif
```

# Android側準備
ネイティブプラグインビルドしてjarをPlugins/Android以下に配置したけどUnityビルドできなかった。
dexを変換できないだの何だの言うからgradleビルドスクリプト弄くり回したしjavaバージョンも確認したがトライアンドエラーに暮れた俺の半日は報われなかった。
だからC#側から遠まわしにjavaを実行することにした。

 Plugins/Android/AndroidManifest.xmlにパーミッションを追加。

```xml
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
```

# PlayMakerのアクション

```csharp:SetBrightness.cs

using System;
using UnityEngine;

#if !UNITY_EDITOR && UNITY_IOS
using System.Runtime.InteropServices;
#endif
namespace HutongGames.PlayMaker.Actions {
	[ActionCategory(ActionCategory.Device)]
	[Tooltip("Sets screen brightness.")]
	public class SetBrightness : FsmStateAction {
		#if !UNITY_EDITOR && UNITY_IOS
		[DllImport("__Internal")]
		private static extern void setBrightness(float brightness);
		#endif
		[ActionSection("Set up")]
		public bool everyFrame;
		[RequiredField]
		public FsmFloat brightness;

		public override void Reset() {
			everyFrame = false;
			brightness = null;
		}

		public override void OnEnter() {
			DoAction();
			if (!everyFrame)
				Finish();
		}

		public override void OnUpdate() {
			DoAction();
		}

		private void DoAction() {
			#if UNITY_EDITOR
			#elif UNITY_IOS
			setBrightness(brightness.Value);
			#elif UNITY_ANDROID
			var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
			var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
			activity.Call("runOnUiThread", new AndroidJavaRunnable(() => {
				var window = activity.Call<AndroidJavaObject>("getWindow");
				var lp = window.Call<AndroidJavaObject>("getAttributes");
				lp.Set("screenBrightness", brightness.Value);
				window.Call("setAttributes", lp);

				int b = 1 + (int)(Math.Min(1, Math.Max(0, brightness.Value)) * 254f); // 1 <= b <= 255
				var system = new AndroidJavaClass("android.provider.Settings.System");
				var contentResolver = activity.Call<AndroidJavaObject>("getContentResolver");
				system.CallStatic("putInt", contentResolver, system.CallStatic<string>("SCREEN_BRIGHTNESS_MODE"), 0);
				system.CallStatic("putInt", contentResolver, system.CallStatic<string>("SCREEN_BRIGHTNESS"), b);
			}));
			#endif
		}
	}
}
```

```csharp:GetBrightness.cs
using System;
using UnityEngine;

#if !UNITY_EDITOR && UNITY_IOS
using System.Runtime.InteropServices;
#endif
namespace HutongGames.PlayMaker.Actions {
	[ActionCategory(ActionCategory.Device)]
	[Tooltip("Gets screen brightness.")]
	public class GetBrightness : FsmStateAction {
		#if !UNITY_EDITOR && UNITY_IOS
		[DllImport("__Internal")]
		private static extern float getBrightness();
		#endif
		[ActionSection("Set up")]
		public bool everyFrame;
		[ActionSection("Store Result")]
		[RequiredField]
		[UIHint(UIHint.Variable)]
		public FsmFloat brightness;

		public override void Reset() {
			everyFrame = false;
			brightness = null;
		}

		public override void OnEnter() {
			DoAction();
			if (!everyFrame)
				Finish();
		}

		public override void OnUpdate() {
			DoAction();
		}

		private void DoAction() {
			#if UNITY_EDITOR
			#elif UNITY_IOS
			brightness.Value = getBrightness();
			#elif UNITY_ANDROID
			var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
			var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
			var system = new AndroidJavaClass("android.provider.Settings.System");
			var contentResolver = activity.Call<AndroidJavaObject>("getContentResolver");
			var b = system.CallStatic<int>("getInt", contentResolver, system.CallStatic<string>("SCREEN_BRIGHTNESS"));
			brightness.Value = b / 255f;
			#endif
		}
	}
}
```

# 終わり
画面輝度を変更するUnityAPIがなかった。せめてサードパーティのアセットがあればいいが一切なかった。
もちろんPlayMakerを通さなくても通常のスクリプトで同じように記述しても問題なく動く。
今回はiOSのネイティブ側の準備が必要でPlayMaker用のC#だけではダメだったのでPlayMakerのEcosystemに追加できないのでこの場で公開した。

## 追記
getとsetの値についてsetで0を入れてしまうと画面が完全に見えなくなったりバグったりって話を聞いたので1以下にならないようにしたけど、getの時とsetの時でint(0~255)とfloat(0~1)間の変換の計算式が違うのが気持ち悪かったかも。