我正在制作一款游戏,我需要知道手机在横向时是向上倾斜(朝向天空)还是向下倾斜(朝向地面)。
我使用这个答案答案链接作为我的起点。但是当应用程序处于横向时,垂直于地面的手机给出值 0,向上或向下倾斜给出值 90。因此,我无法确定用户是否向上或向下倾斜手机。
如果有人想知道我是如何实现我的传感器的,那么他可以询问,但我认为这么多的背景足以理解这个问题。
这就是我找到正确解决方案的方法。首先,我创建了一个与多个传感器一起使用的抽象类(从 Phillip Lanckner Youtube 学习)
abstract class MeasurableSensor(protected val sensorType : Int) {
protected var onSensorValuesChanged : ((List<Float>) -> Unit)? = null
abstract val doesSensorExist : Boolean
abstract fun startListening()
abstract fun stopListening()
fun setOnSensorValuesChangedListener(listener : ((List<Float>) -> Unit)){
onSensorValuesChanged = listener
}
}
abstract class AndroidSensor(
private val context : Context,
private val sensorFeature : String,
sensorType : Int
) : MeasurableSensor(sensorType) , SensorEventListener{
override val doesSensorExist: Boolean
get() = context.packageManager.hasSystemFeature(sensorFeature)
private lateinit var sensorManager: SensorManager
private var sensor : Sensor? = null
override fun startListening() {
if(!doesSensorExist) return
if(!::sensorManager.isInitialized && sensor == null){
sensorManager = context.getSystemService(SensorManager :: class.java) as SensorManager
sensor = sensorManager.getDefaultSensor(sensorType)
}
sensor?.let {
sensorManager.registerListener(this , it , SensorManager.SENSOR_DELAY_NORMAL)
}
}
override fun stopListening() {
if(!doesSensorExist || !::sensorManager.isInitialized) return
sensorManager.unregisterListener(this)
}
override fun onSensorChanged(event: SensorEvent?) {
if(!doesSensorExist) return
if (event?.sensor?.type == sensorType){
onSensorValuesChanged?.invoke(event.values.toList())
}
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) = Unit
}
这些是在 3D 平面中获得手机精确方向所需的两个传感器。
class AccelerometerSensor(
context: Context
) : AndroidSensor(
context,
PackageManager.FEATURE_SENSOR_ACCELEROMETER,
Sensor.TYPE_ACCELEROMETER
)
class MagnetometerSensor(
context: Context
) : AndroidSensor(
context,
PackageManager.FEATURE_SENSOR_ACCELEROMETER,
Sensor.TYPE_MAGNETIC_FIELD
)
在viewModel中实现
@HiltViewModel
class ViewModel @Inject constructor(
private val accelerometerSensor : MeasurableSensor,
private val magnetometerSensor : MagnetometerSensor,
) : ViewModel() {
private val _orientation = MutableStateFlow(OrientationState.PERPENDICULAR)
val orientation : StateFlow<OrientationState> get() = _orientation
private val accelerometerReading = FloatArray(3)
private val magnetometerReading = FloatArray(3)
private val rotationMatrix = FloatArray(9)
private val orientationAngles = FloatArray(3)
fun startSensors(){
accelerometerSensor.startListening()
magnetometerSensor.startListening()
initListeners()
}
fun stopSensors(){
accelerometerSensor.stopListening()
magnetometerSensor.stopListening()
}
private fun initListeners() {
accelerometerSensor.setOnSensorValuesChangedListener { accelerometerReading ->
this.accelerometerReading[0] = accelerometerReading[0]
this.accelerometerReading[1] = accelerometerReading[1]
this.accelerometerReading[2] = accelerometerReading[2]
computeOrientation()
}
magnetometerSensor.setOnSensorValuesChangedListener { magnetometerReading ->
this.magnetometerReading[0] = magnetometerReading[0]
this.magnetometerReading[1] = magnetometerReading[1]
this.magnetometerReading[2] = magnetometerReading[2]
}
}
private fun computeOrientation() {
SensorManager.getRotationMatrix(
rotationMatrix,
null,
accelerometerReading,
magnetometerReading
)
// "rotationMatrix" now has up-to-date information.
SensorManager.getOrientation(rotationMatrix, orientationAngles)
// "orientationAngles" now has up-to-date information.
val (_ , _ , y) = orientationAngles
// Values used here , are the ones needed by me , you may or may not need the same values
val orientationState = when (y.radToDegrees()) {
in 226..314 -> OrientationState.PERPENDICULAR
in 315..359, in 0..44 -> OrientationState.UPWARDS
in 136..225 -> OrientationState.DOWNWARDS
else -> OrientationState.PERPENDICULAR
}
viewModelScope.launch {
_orientation.emit(orientationState)
}
}
将 rad 转换为度数的扩展函数
fun Float.radToDegrees() : Int {
return (Math.toDegrees(this.toDouble()).toInt() + 360) % 360
}
Phillip Lackner 视频可用于详细学习传感器管理,计算设备方向可在 android 文档
中进一步了解更新:
一个简单的解决方案是使用加速度计来获取当前z轴的重力。
override fun onSensorChanged(event: SensorEvent) {
if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) {
// Check the orientation based on accelerometer data
val x = event.values[0]
val y = event.values[1]
val z = event.values[2]
// Assuming the device is facing up if the Z-axis is close to 9.81 (gravity)
val isFacingUp = z > 9.0
// Assuming the device is facing down if the Z-axis is close to -9.81 (gravity)
val isFacingDown = z < -9.0
if (isFacingUp) {
println("I'm facing up!")
} else if (isFacingDown) {
println("I'm facing down!")
} else {
println("I'm facing another direction!")
}
}
}
旧答案:
如果您遵循 https://developer.android.com/guide/topics/sensors/sensors_position#sensors-pos-orient 中的 Google 示例,您应该获得足够的信息来获取设备的准确方向。
相关数据可以在rotationMatrix(或orientationAngles中的角度)变量中找到。
如果将手中的设备面朝下绕 y 轴旋转,则 x 和 z 轴矢量指向相反的方向。
显示朝上(四舍五入值)
rotationMatrix: [0, 1, 0, -1, 0, 0, 0, 0, 1]
显示朝下(四舍五入值)
rotationMatrix: [0, -1, 0, -1, 0, 0, 0, 0, -1]
这是一个很好的可视化方向的工具: https://ninja-calc.mbedded.ninja/calculators/mathematics/geometry/3d-rotations