UE4:iOS CLLocationManagerを使って、緯度経度の値を取得する
UE4でGPS情報取得
UE4からiOS CLLocationManagerを使って、緯度経度の値を取得してみました。
iOSのObjective-Cのapiを呼び出しているのですが、色々わかったことがあったので記します。
わかったこと
- プラットホームがiOSの場合にのみ、ビルドされるように、iOS特有のコードは、#if PLATFORM_IOS 〜 #endif で囲むこと。
- xcodeのProjectは、UE4側で管理されているので、Objective-Cのクラスを追加する場合もUE4EditorのAdd Code To Projectで親クラスNoneで*.cpp/*.hを追加して、そこにObjective-Cのコードを記述する。既存のクラスの追加は、Add Code To Projectで同じ名前のファイルを追加して、そこにコードをコピペしました。(ちゃんとした方法があるかも)
- plistは、UE4側で管理され自動生成されるので、追加記述する必要がある場合は、UE4EditorのProjectSettings->iOS->ExtraPListData->AdditionalPListDataに記述する。記述は、PlistのXML形式で書く必要があり説明がない。

- framework(CoreLocation.frameworkなど)の追加は、UE4側で生成された*.Build.csで追加指定を記述しないと追加されない。
if (Target.Platform == UnrealTargetPlatform.IOS)
{
// for GPS
PublicFrameworks.Add("CoreLocation");
}
- C++のActorのクラスからObjective-Cのメソッドやクラスは普通に呼び出せた。
- UE4のアクターの実行は、どうもメインスレッドではないので、iOSのapiを利用するクラスは、メインスレッドで生成して実行しないとうまく動かない。
dispatch_async(dispatch_get_main_queue(), ^{
MyLocationManager* myLocMgr=[MyLocationManager sharedInstance];
[myLocMgr start];
});
サンプルコード
UE4にObjective-Cのクラスやapiを組み込む時の参考にソースも一部載せておきます。
- GPS情報を取得する為のObjective-CのMyLocationManagerクラスをGPSActorからメインスレッド上に生成しています。
- GPSActorクラスで、緯度と経度をUTextRenderComponentで表示しています。
- CLLocationManagerを使えるようにするためplistに以下の設定を追加(AdditionalPListDataに記述する。)
<key>NSLocationAlwaysUsageDescription</key>\n<string>ddd</string>\n<key>NSLocationWhenInUseUsageDescription</key>\n<string>xyz</string>\n
- CoreLocation.frameworkを追加するため、プロジェクト名.Build.cs に追加設定を記述
GPSActor.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "GameFramework/Actor.h"
#include "GPSActor.generated.h"
UCLASS()
class TESTIOSAPI_API AGPSActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AGPSActor();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called every frame
virtual void Tick( float DeltaSeconds ) override;
private:
UTextRenderComponent* InfoText;
void UpdateInfo();
float WaitTime;
int UpdateCnt;
};
GPSActor.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "TestIOSAPI.h"
#include "GPSActor.h"
#include "MyLocationManager.h"
// Sets default values
AGPSActor::AGPSActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
InfoText=CreateAbstractDefaultSubobject<UTextRenderComponent>(TEXT("InfoText"));
InfoText->SetHorizontalAlignment(EHTA_Center);
InfoText->SetWorldSize(150.0f);
InfoText->SetTextRenderColor(FColor::Red);
InfoText->AttachTo(RootComponent);
RootComponent=InfoText;
}
// Called when the game starts or when spawned
void AGPSActor::BeginPlay()
{
Super::BeginPlay();
#if PLATFORM_IOS
//MyLocationManagerは、メインスレッドで動かさないとうまく動かないです。
dispatch_async(dispatch_get_main_queue(), ^{
MyLocationManager* myLocMgr=[MyLocationManager sharedInstance];
[myLocMgr start];
});
#endif
WaitTime=0;
}
// Called every frame
void AGPSActor::Tick( float DeltaTime )
{
Super::Tick( DeltaTime );
WaitTime+=DeltaTime;
if(WaitTime>3.0){
WaitTime=0.0;
UpdateInfo();
}
}
void AGPSActor::UpdateInfo(){
#if PLATFORM_IOS
// Objective-Cのコードは、C++から普通によべるみたい。
float latitude,longitude;
MyLocationManager* myLocMgr=[MyLocationManager sharedInstance];
CLLocation *location = [myLocMgr.locationManager location];
CLLocationCoordinate2D coordinate = [location coordinate];
NSLog(@"request ReadTextData (%@ ,alti:%f)%d,%d",[location description],location.altitude,myLocMgr.updateCount,myLocMgr.errorCount);
InfoText->SetText(FString::Printf(TEXT("GPSValue(%f,%f)%d,%d,%d"),coordinate.latitude,coordinate.longitude,myLocMgr.updateCount,myLocMgr.errorCount,UpdateCnt));
#endif
}
MyLocationManager.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#if PLATFORM_IOS
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#endif
#if PLATFORM_IOS
@class MyLocationManager;
@interface MyLocationManager : NSObject<CLLocationManagerDelegate> {
@private
int updateCount_;
int errorCount_;
CLLocationManager* locationManager_;
}
@property (readonly) CLLocationManager* locationManager;
@property (assign) int updateCount;
@property (assign) int errorCount;
//位置取得サービスが有効かどうか
-(BOOL)serviceEnabled ;
//開始
-(void)start;
//停止
-(void)stop;
//Class Method
+ (MyLocationManager*)sharedInstance;
@end
#endif
MyLocationManager.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "TestIOSAPI.h"
#include "MyLocationManager.h"
#if PLATFORM_IOS
@implementation MyLocationManager
@synthesize locationManager=locationManager_;
@synthesize updateCount=updateCount_;
@synthesize errorCount=errorCount_;
//初期化
-(id)init {
if(self=[super init]){
// 位置取得関係初期か
//NotificationCenterに一時停止時と再開時に位置情報処理の再開停止関数を登録
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidBecomeActive)
name:UIApplicationDidBecomeActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillResignActive)
name:UIApplicationWillResignActiveNotification
object:nil];
locationManager_=[[CLLocationManager alloc] init];
locationManager_.delegate = self;
locationManager_.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
locationManager_.activityType = CLActivityTypeFitness;
locationManager_.pausesLocationUpdatesAutomatically = YES;
locationManager_.distanceFilter = kCLDistanceFilterNone;
[self start];
updateCount_++;
}
return self;
}
//位置取得サービスが有効かどうか
-(BOOL)serviceEnabled {
return locationManager_!=nil && [CLLocationManager locationServicesEnabled];
}
//アプリが再開するときNotificationCenterからよばれる
-(void) applicationDidBecomeActive {
[self start];
}
//アプリが一時停止するときNotificationCenterからよばれる
-(void) applicationWillResignActive {
[self stop];
}
// 位置情報更新時によばれる。ずっと同じ位置にいるとよばれない
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
{
if([locations count]>0){
CLLocation* newLocation=[locations objectAtIndex:0];
//緯度・経度を出力
NSLog(@"didUpdateToLocation latitude=%f, longitude=%f",
[newLocation coordinate].latitude,
[newLocation coordinate].longitude);
// [locationManager_ stopUpdatingLocation];
updateCount_++;
}
}
// 測位失敗時や、位置情報の利用をユーザーが「不許可」とした場合などに呼ばれる
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error{
NSLog(@"locationManager:didFailWithError");
errorCount_++;
}
- (void)locationManager:(CLLocationManager *)manager
didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
if (status == kCLAuthorizationStatusNotDetermined) {
if([locationManager_ respondsToSelector:@selector(requestAlwaysAuthorization)]) {
updateCount_++;
[locationManager_ startUpdatingLocation];
[locationManager_ requestAlwaysAuthorization];
}
}
}
-(void)start {
if([self serviceEnabled]){
[locationManager_ startUpdatingLocation];
//didChangeAuthorizationStatusでよんでるけど、うまくいかなかったので、ここでもリクエストする。
if([locationManager_ respondsToSelector:@selector(requestAlwaysAuthorization)]) {
[locationManager_ requestAlwaysAuthorization];
}
}
}
-(void)stop {
if([self serviceEnabled]){
[locationManager_ stopUpdatingLocation];
}
}
///=========================================
//シングルトン制御用メソッド
//=========================================
static MyLocationManager* sharedInstance_=nil;
+ (MyLocationManager*)sharedInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance_=[[MyLocationManager alloc] init];
});
return sharedInstance_;
}
@end
#endif
TestIOSAPI.Build.cs (プロジェクト名.Build.cs)
// Fill out your copyright notice in the Description page of Project Settings.
using UnrealBuildTool;
public class TestIOSAPI : ModuleRules
{
public TestIOSAPI(TargetInfo Target)
{
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
PrivateDependencyModuleNames.AddRange(new string[] { });
// Uncomment if you are using Slate UI
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
// Uncomment if you are using online features
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
// if ((Target.Platform == UnrealTargetPlatform.Win32) || (Target.Platform == UnrealTargetPlatform.Win64))
// {
// if (UEBuildConfiguration.bCompileSteamOSS == true)
// {
// DynamicallyLoadedModuleNames.Add("OnlineSubsystemSteam");
// }
// }
// CoreLocation.frameworkを追加するために追加したコード
if (Target.Platform == UnrealTargetPlatform.IOS)
{
// for GPS
PublicFrameworks.Add("CoreLocation");
}
//ここまで
}
}