最新文章
Cocos2d-x游戏开发实例详解7:对象释放时机
03-25 13:59
Cocos2d-x游戏开发实例详解6:自动释放池
03-25 13:55
Cocos2d-x游戏开发实例详解5:神奇的自动释放
03-25 13:49
Cocos2d-x游戏开发实例详解4:游戏主循环
03-25 13:44
Cocos2d-x游戏开发实例详解3:无限滚动地图
03-25 13:37
Cocos2d-x游戏开发实例详解2:开始菜单续
03-25 13:32
unity3d 获取手机地理位置
在 Unity 的某些场景中,若需要定位地理位置,Unity 本身并未提供相应的接口。以安卓平台为例(IOS 暂未研究),此时需要使用 Java 编写获取地理位置的方法,再与 Unity 进行交互,从而在 Unity 脚本中获取当前手机的地理位置。在实际开发中,为了方便,我们可以直接调用百度的 API 来获取地理信息。
获取地理位置的方式
获取地理位置主要有以下四种方式:
- 手机信号塔数据:当移动设备的 GPS 芯片无法接收到 GPS 信号时,移动设备会与所连接的手机信号塔进行通讯,并估算与信号塔之间的距离,以此不断报告自身的地理位置。不过,通过这种方法获得的地理位置数据不如纯粹的 GPS 数据精确。
- WiFi 连接:这是一种能够精确获得地理位置数据的方法,但需要有效的 WiFi 热点。WiFi 的地址与 GPS 坐标一一对应,可以准确地标示出用户所处的位置。这也是很多零售商愿意提供免费公共 WiFi 服务的原因,以便发布店内移动广告。
- IP 地址:地理位置也可通过与数据连接有关的 IP 地址来获取。不过,这种方法的准确性会因运营商而异,可靠性也比上述方法低一些。
- GPS:具有精确的定位效果,且无需连接网络,但连接过程比较耗时。
使用百度 API 获取地理位置
使用百度 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
下载百度定位的 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:
<service android:name="com.baidu.location.f" android:enabled="true" android:process=":remote">
</service>
同时,声明使用权限:
<!-- 这个权限用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
<!-- 这个权限用于访问 GPS 定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
<!-- 用于访问 wifi 网络信息,wifi 信息会用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<!-- 获取运营商信息,用于支持提供运营商信息相关的接口-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<!-- 这个权限用于获取 wifi 的获取权限,wifi 信息会用来进行网络定位-->
<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" />
<!-- SD 卡读取权限,用户写入离线定位数据-->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>
<!-- 允许应用读取低级别的系统日志文件 -->
<uses-permission android:name="android.permission.READ_LOGS"></uses-permission>
4. 打包并集成到 Unity 项目
将该安卓项目的代码打成 jar 包,然后放在 Unity 项目资源下的 Plugins->Android 目录下。再将 libs(里面只能放百度的第三方 jar 包,不要把 Unity 的 class.jar 和 Android 的 jar 包放在里面)、res 文件夹、AndroidManifest 也放在 Android 目录下。
5. 编写 Unity 脚本
在场景中放置一个空物体,命名为 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<AndroidJavaObject>("currentActivity");
}
// 回调函数,接收到地理信息,展示在页面上。
public void AddressCallback(string jsonStr) {
Debug.Log("zs : " + jsonStr);
geography = "第" + (++count).ToString() + "次接受信息:" + jsonStr;
}
void OnGUI() {
GUI.Label(new Rect(0, 0, 700, 300), geography, style);
// 启动地理服务
if (GUI.Button(new Rect(0, 400, 60, 30), " 启 动 ")) {
jo.Call("startLocationService");
}
// 关闭地理服务
if (GUI.Button(new Rect(140, 400, 60, 30), " 停 止 ")) {
jo.Call("stopLocationService");
}
}
}
发布项目
完成上述步骤后,就可以进行项目发布了。对于安卓的发布,需要安装 Android 的 SDK,具体安装步骤可参考网上的众多教程。需要注意的是,若打开 GPS 和网络,系统会优先使用 GPS 进行定位。
至此,我们就可以在 Unity3D 项目中获取手机的地理位置了。