unity3d 获取地理位置

2015年02月01日 09:23 0 点赞 1 评论 更新于 2017-05-09 14:23

在Unity的某些场景中,若需要获取地理位置,Unity本身并未提供相应的接口。以安卓平台为例(iOS暂未研究),此时可使用Java编写获取地理位置的方法,再与Unity进行交互,从而在Unity脚本中获取当前手机的地理位置。在实际开发中,为了方便,我们可以直接调用百度的API来获取地理信息。

地理位置获取方式

获取地理位置主要有以下四种方式:

  1. 手机信号塔数据:当移动设备的GPS芯片无法接收到GPS信号时,移动设备会与所连接的手机信号塔进行通讯,并估算与信号塔之间的距离,以此不断报告自身的地理位置。不过,通过这种方法获得的地理位置数据不如纯粹的GPS数据精确。
  2. WiFi连接:这是一种能够精确获得地理位置数据的方法,但需要有效的WiFi热点。WiFi的地址与GPS坐标是一一对应的,它可以准确地标示出用户所处的位置。因此,很多零售商愿意提供免费的公共WiFi服务,以便发布店内移动广告。
  3. IP地址:地理位置也可以通过与数据连接有关的IP地址来获得。不过,这种方法的准确性会因运营商而异,可靠性也比上述几种方法要低一些。
  4. GPS:定位精确,无需连接网络,但连接过程比较耗时。

使用百度API通过IP地址获取地理位置

使用百度API调用时,首先需要申请一个key,具体申请流程可前往官网操作。根据IP地址调用比较方便,可直接使用HTTP的方式发送请求,然后返回JSON格式的数据。以下是相关代码示例:

public class AddressTool
{
// ip为空,默认为本机ip
public static IEnumerator GetAddressFromIP(string ipAddress, AddressForQueryIPFromBaidu data)
{
string baiduKey = "42c43828c02a9f6144dd305643ca73b8";
// ip:ip地址(可选,ip不出现,或者出现且为空字符串的情况下,会使用当前访问者的IP地址作为定位参数),ak:用户密钥,
// coor:输出的坐标格式(可选,coor不出现时,默认为百度墨卡托坐标;coor=bd09ll时,返回为百度经纬度坐标)
string url = "http://api.map.baidu.com/location/ip?ak=" + baiduKey + "&coor=bd09ll&ip=202.198.16.3";
WWW w = new WWW(url);
// 超时时间
float t = Time.time;
while (!w.isDone)
{
if (Time.time - t > 15f)
{
Debug.Log("server time out!");
w.Dispose();
yield break;
}
yield return null;
}
// 接收http请求返回的数据(json格式)
string jsonData = w.text;
// 解析成对象
AddressForQueryIPFromBaidu result = (AddressForQueryIPFromBaidu)ValueParse.ParseJsonToClass(typeof(AddressForQueryIPFromBaidu), jsonData);
}
}

#region AddressForQueryIPFromBaidu
public class AddressForQueryIPFromBaidu
{
// 地址
public string address { get; set; }
// 详细内容
public Content content { get; set; }
// 返回状态码
public string status { get; set; }
}

public class Content
{
// 简要地址
public string address { get; set; }
// 详细地址信息
public Address_Detail address_Detail { get; set; }
// 百度经纬度坐标值
public Point point { get; set; }
}

public class Address_Detail
{
// 城市
public string city { get; set; }
// 百度城市代码
public string city_Code { get; set; }
// 区县
public string district { get; set; }
// 省份
public string province { get; set; }
// 街道
public string street { get; set; }
// 门址
public string street_Number { get; set; }
}

public class Point
{
public string x { get; set; }
public string y { get; set; }
}
#endregion

需要注意的是,IP定位的结果精度较差,主要用于获取省份或者城市的位置信息。

使用百度Android定位SDK获取地理位置

百度Android定位SDK利用设备当前的GPS信息(GPS定位)、网络信息(基站定位和Wi-Fi定位)完成定位。开发者在应用中成功集成百度定位SDK(需将一个armeabi的文件夹放在libs中)后,即可通过定位SDK的接口向百度定位服务请求位置信息。

步骤如下:

  1. 下载百度定位的SDK:并导入到安卓工程中。
  2. 相关Android代码示例
    package com.zs.address;
    

import org.json.JSONException; import org.json.JSONObject; import android.annotation.SuppressLint; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.baidu.location.BDLocation; import com.baidu.location.BDLocationListener; import com.baidu.location.LocationClient; import com.baidu.location.LocationClientOption; import com.baidu.location.LocationClientOption.LocationMode; import com.unity3d.player.UnityPlayer; import com.unity3d.player.UnityPlayerActivity;

@SuppressLint("NewApi") public class MainActivity extends UnityPlayerActivity { public LocationClient mLocationClient = null; public BDLocationListener mListener = new MyLocationListener();

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.activity_main); // initView(); // 初始化百度定位SDK initLocationService(); }

@SuppressLint("NewApi") public void initLocationService() { // 声明LocationClient类 mLocationClient = new LocationClient(getApplicationContext()); mLocationClient.registerLocationListener(mListener); // 注册监听函数

LocationClientOption option = new LocationClientOption(); option.setLocationMode(LocationMode.Hight_Accuracy); // 设置定位模式 option.setCoorType("gcj02"); // 返回的定位结果,默认值gcj02 option.setScanSpan(5000); // 设置发起定位请求的间隔时间为5000ms option.setIsNeedAddress(true); // 返回的定位结果包含地址信息 option.setNeedDeviceDirect(true); // 返回的定位结果包含手机机头的方向 option.setOpenGps(true); mLocationClient.setLocOption(option); mLocationClient.requestLocation(); }

// 启动定位服务,给unity调用 public void startLocationService() { mLocationClient.start(); }

// 停止定位服务,给unity调用 public void stopLocationService() { mLocationClient.stop(); }

class MyLocationListener implements BDLocationListener { @Override public void onReceiveLocation(BDLocation location) { // public int getLocType ( )返回值: // 61 : GPS定位结果 // 62 : 扫描整合定位依据失败。此时定位结果无效。 // 63 : 网络异常,没有成功向服务器发起请求。此时定位结果无效。 // 65 : 定位缓存的结果。 // 66 : 离线定位结果。通过requestOfflineLocaiton调用时对应的返回结果 // 67 : 离线定位失败。通过requestOfflineLocaiton调用时对应的返回结果 // 68 : 网络连接失败时,查找本地离线定位时对应的返回结果 // 161: 表示网络定位结果 // 162~167: 服务端定位失败 // 502:key参数错误 // 505:key不存在或者非法 // 601:key服务被开发者自己禁用 // 602:key mcode不匹配 // 501~700:key验证失败 if (location == null) return; JSONObject jo = new JSONObject(); try { jo.put("error_code", location.getLocType()); jo.put("time", location.getTime()); jo.put("latitude", location.getLatitude()); // 获取维度 jo.put("lontitude", location.getLongitude()); // 获取经度 jo.put("radius", location.getRadius()); jo.put("direction", location.getDirection()); // 只有使用GPS定位的情况下,获取移动速度,当前连接的卫星编号 if (location.getLocType() == BDLocation.TypeGpsLocation) { JSONObject gpsInfo = new JSONObject(); gpsInfo.put("speed", location.getSpeed()); gpsInfo.put("satellite", location.getSatelliteNumber()); jo.put("gpsInfo", gpsInfo); } else if (location.getLocType() == BDLocation.TypeNetWorkLocation) { // 只有使用网络定位的情况下,才能获取当前位置的反地理编码描述。 // 自定位SDK2.6版本之后,支持获取省/市/区分级地理信息: JSONObject netWorkInfo = new JSONObject(); netWorkInfo.put("address", location.getAddrStr()); // 获取反地理编码 netWorkInfo.put("province", location.getProvince()); // 获取省份信息 netWorkInfo.put("city", location.getCity()); // 获取城市信息 netWorkInfo.put("city_code", location.getCityCode()); // 获取城市编码 netWorkInfo.put("district", location.getDistrict()); // 获取区县信息 netWorkInfo.put("street", location.getStreet()); // 获取街道地址 netWorkInfo.put("street_number", location.getStreetNumber()); // 获取街道门牌号 jo.put("netWorkInfo", netWorkInfo); } } catch (JSONException e1) { Log.i("address", e1.toString()); }

Log.i("address", jo.toString()); // 将获取的地理信息返回给unity UnityPlayer.UnitySendMessage("address", "AddressCallback", jo.toString()); // text.setText(jo.toString()); } }

private void requestLocation() { mLocationClient.requestLocation(); }

@Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }

3. **设置AndroidManifest.xml**:
- 在application标签中声明service组件,这是百度SDK的定位service:
- 声明使用权限:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission> <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission> <uses-permission android:name="android.permission.READ_LOGS"></uses-permission>

4. **将项目做成可运行的app**:把MainActivity的一些注释打开,然后继承Activity,就可以直接做成app在手机上运行。该项目demo可在本文最后获取。
5. **将安卓项目代码集成到Unity项目**:
- 把该安卓项目的代码打成jar包,然后放在Unity项目资源下的Plugins->Android目录下。
- 再把libs(里面只能放百度的第三方jar包,不要把Unity的class.jar和Android的jar包放在里面)、res文件夹、AndroidManifest,也放在Android目录下。
- 在场景中放上一个空物体,名为‘address’(必须和Java代码中“UnityPlayer.UnitySendMessage("address", "AddressCallback", jo.toString());”保持一致),编写一个脚本“AddressTest.cs”,绑在该空对象上面。脚本代码如下:

using UnityEngine; using System.Collections; using address;

public class AddressTest : MonoBehaviour { // 地理信息 private string geography = "lala haha wawa"; private GUIStyle style = new GUIStyle(); private int count = 0; private AndroidJavaObject jo;

public void Awake() { // GUI样式 style.fontSize = 30; style.wordWrap = true; // 初始化安卓的MainActivity。下面两句话是固定的,不能变动 AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); jo = jc.GetStatic("currentActivity"); } }


通过以上步骤,我们就可以在Unity项目中实现获取地理位置的功能。

作者信息

boke

boke

共发布了 1025 篇文章