












畀馬仔收購『twitter』後改名『x』.注册吾使填hk電話冧靶, 衹要gmail郵箱.
- 撳『使用Google帳戶繼續』
- 選揀gmail郵箱登記帳戶
- 『言語』揀漢字.
- 填『牛一』
- 填『使用者名稱』, 填域名『.』替换『_』. 例『bookcard_net』
- 允許x存取聯絡人.
BOOKCARD
安桌/Android
注册『Facebook』要HK電話冧靶.可能區域限制係『google play』下載吾到『facebook.apk』,可用下面URL地址下載後.
https://apkcombo.app/tw/facebook/com.facebook.katana/download/apk |
用USB磁碟复制入手機『DOWNLOAD』資料夾.係USB磁碟係装到app.
/storage/emulated/0/download |
為左張3HK電話咭,祗有買部『Google Pixel5』.鋪頭話係官翻機,冇黑色得番綠色.部機擺得耐冇曬電,係大陸WIFI連線受限,實際可上網.
用『Google Pixel』手機要張『流動數據』『收發短信』『接打電話』叁合壹HK電話卡.手機要更新GOOGLE PLAY等程式先用得. 部機得128GB驚慢,實際運行流畅. 等有銀諗買『Google Pixel8 Pro 1TB』.首先HK電話罧靶, 愛蒞注冊Gmail郵箱.
装『Instagram』要『WhatsApp』收驗証碼. 且佢保安系統, 係新機登入要填舊舊機驗証碼, 登出舊機. 如果舊機跌左就大鑊.
後續, 冇過两日收到警告, 第日畀人停用,提出申訴後第日帳戶恢复,可能係『Instagram』判斷你係真人.
注册『WhatsApp』,要HK電話冧靶, SIM咭可『收短信』同埋『上網』流動數據.
随著『Android studio』更新版本,『Gradle』需升級.
https://services.gradle.org/distributions/gradle-8.2.1-bin.zip |
C:\Users\admin\.gradle\wrapper\dists\gradle-8.2.1-bin\5hap6b9n41hkg4jeh2au2pllh\gradle-8.2.1-bin.zip |
Connection timed out: no further information. If you are behind an HTTP proxy, please configure the proxy settings either in IDE or Gradle. |
Android studio近日彈出gradle版本晤兼容.修改工程配置. 如果唔得重裝『Android Studio』.
Android Studio版本 | Android Gradle Plugin Version插件版本 |
Hedgehog | 2023.1.1 | 3.2~8.2 |
Giraffe | 2022.3.1 | 3.2~8.1 |
Flamingo | 2022.2.1 | 3.2~8.0 |
Electric Eel | 2022.1.1 | 3.2~7.4 |
Dolphin | 2021.3.1 | 3.2~7.3 |
Android Gradle Plugin Version插件版本 | Gradle Version |
8.1 | 8.0 |
8.0 | 8.0 |
7.4 | 7.5 |
7.4 | 7.4 |
7.2 | 7.3.3 |
7.1 | 7.2 |
7.0 | 7.0 |
4.2.0+ | 6.7.1 |
The project is using an incompatible version (AGP 8.1.0) of the Android Gradle plugin. Latest supported version is AGP 8.0.0 |
『ZLIB』開源『壓縮』同『解壓』程式庫, 支持『DEFLATE』冇損壓縮算法,佢混合『LZ77算法』同『霍夫曼編碼』.
『DEFLATE』壓縮算法冇專利權.畀人大量應用係『網络』『圖檔』『文檔』『影片』.
.PNG/ Libpng | 圖檔解壓 |
.ZIP | 壓縮檔 |
.tar | 壓縮檔 |
.gz | 壓縮檔 |
HTTP | 壓縮傅送 |
FFmpeg | 影片解壓 |
下载『ZLIB』
http://www.zlib.net/ |
http://www.zlib.net/zlib-1.2.13.tar.gz |
『Android studio』內置『ZLIB』, 唔使下載.但要係『CMakeLists.txt』增添『zlib』庫.
CMakeLists.txt文檔 | |
find_library( z-lib z ) | 搜索zlib |
target_link_libraries( ${z-lib} ) | 連接zlib |
包含『ZLIB』頭文檔
#include <zlib.h> | Zlib-api |
#include <zconf.h> |
『z_stream』壓縮同解壓皆需此結构體
z_stream stream; | zlib流結构體 |
stream.zalloc = Z_NULL; | NULL用默認記憶體分配函数 |
stream.zfree = Z_NULL; | NULL用默認記憶體释放函数 |
stream.opaque = Z_NULL; | |
stream.next_in = (Bytef*)sour; | 蒞源 |
stream.avail_in = (uInt)sour_length; | 蒞源長 |
stream.next_out = dest; | 輸出 |
stream.avail_out = (uInt)*dest_length; | 輸出長 |
『ZLIB』壓縮分叁步
壓縮 | |
deflateInit(&stream, level) | 分配記憶體,level壓縮等級 |
deflate(&stream, flush); | 壓縮數據, flush設0 |
deflateEnd(&stream); | 释放記憶體 |
int deflateInit2( | deflateInit()加强版 |
z_streamp strm, | zlib流結构體 |
int level, | level壓縮等級0~9.
0:速度快,唔壓縮. 9:速度慢,壓縮率高. |
int method, | 壓縮算法僅支緩Z_DEFLATED |
int windowBits, | 處理RAW DEFLATE手法. |
int memLevel, | 指定記憶體分配MAX_MEM_LEVEL |
int strategy)); | 壓縮策略,僅影響壓縮比.默認Z_DEFAULT_STRATEGY |
level | 壓縮等級 |
#define Z_NO_COMPRESSION 0 | 唔壓縮 |
#define Z_BEST_SPEED 1 | 高速,低壓縮率 |
#define Z_BEST_COMPRESSION 9 | 高壓縮率, 慢速 |
#define Z_DEFAULT_COMPRESSION (-1) | 默認壓縮 |
windowBits | 處理RAW DEFLATE手法. |
8~15: | 純deflate壓縮 |
-8~-15: | zlib頭 + deflate + zlib尾 |
> 16: | Gzip頭+ deflate + Gzip尾 |
method | 壓縮算法 |
#define Z_DEFLATED 8 | DEFLATE冇損壓縮 |
memLevel | 記憶體分配 |
MemLevel=1 | 最小記憶體,速度慢壓縮比低 |
MemLevel=9
#define MAX_MEM_LEVEL 9 |
最大記憶體,最佳速度 |
MemLevel=8 | 默認值 |
strategy | 壓縮算法設定 |
#define Z_FILTERED 1 | 僅FILTERED生成數據 |
#define Z_HUFFMAN_ONLY 2 | 僅霍夫曼編碼 |
#define Z_RLE 3 | 匹配長度=1 |
#define Z_FIXED 4 | 禁霍夫曼編碼 |
#define Z_DEFAULT_STRATEGY 0 | 默認壓縮設定 |
『ZLIB』解壓分叁步
解壓 | |
inflateInit(&stream) | 分配記憶體 |
inflate(&stream, Z_NO_FLUSH); | 解压數據 |
inflateEnd(stream); | 释放記憶體 |
返回碼 | |
#define Z_OK 0 | |
#define Z_STREAM_END 1 | 結束 |
#define Z_NEED_DICT 2 | 愛密碼 |
#define Z_ERRNO (-1) | |
#define Z_STREAM_ERROR (-2) | |
#define Z_DATA_ERROR (-3) | 加密數據損壞壞,或缺失. |
#define Z_MEM_ERROR (-4) | 唔够記憶體 |
#define Z_BUF_ERROR (-5) | 唔够緩存 |
#define Z_VERSION_ERROR (-6) |
解壓示例
int Uncompress(PBYTE dest,int * dest_length,PBYTE sour,int sour_length)
{
z_stream stream;
int ret;
stream.zalloc = (alloc_func)0;
stream.zfree = (free_func)0;
stream.opaque = (voidpf)0;
stream.next_in = (Bytef*)sour;
stream.avail_in = (uInt)sour_length;
stream.next_out = dest;
stream.avail_out = (uInt)*dest_length;
MAX_MEM_LEVEL
ret = inflateInit2(&stream, 16+MAX_WBITS);
if (ret != Z_OK)
return ret;
ret = inflate(&stream, Z_NO_FLUSH);// 解压
*dest_length = stream.total_out;
inflateEnd(&stream);
return ret;
}
壓缩示例
bool Compress(PBYTE dest,int * dest_length,PBYTE sour,int sour_length, int level)
{
int ret, flush;
int sour_offset,dest_offset;
int have;
z_stream stream;
BYTE in[ZIP_CHUNK];
BYTE out[ZIP_CHUNK];
stream.zalloc = Z_NULL;// 内存分配函数
stream.zfree = Z_NULL;// 内存释放函数
stream.opaque = Z_NULL;
stream.next_in = (Bytef*)sour;
stream.avail_in = (uInt)sour_length;
stream.next_out = dest;
stream.avail_out = (uInt)*dest_length;
ret = deflateInit(&stream, level);// 内存分配
if (ret != Z_OK)
return false;
ret = deflate(&stream, flush); // 进行压缩
*dest_length = stream.total_out;
deflateEnd(&stream);// 释放内存
return ret;
}
『Android studio』內置架餐『Image Asset』. 畀幅圖檔佢,『Image Asset』帮你自動生成各式圖檔.
揀『Launcher Icons(Legacy only)』冚辦闌圖檔背景透明.
『Android 8.0』及以上,程式圖檔强制背景色,揀『Launcher Icons(Adaptive and Legacy)』.
Android 8.0及以上 | 圖檔背景白色 | Launcher Icons(Adaptive and Legacy) |
Android 7.1及以下 | 圖檔背景透明 | Launcher Icons(Legacy only) |
Configure Image Asset | Android 8.0(API 26) 及以上 |
Icon type | Launcher Icons(Adaptive and Legacy) |
Name | ic_launcher |
Layer name | ic_launcher_foreground |
Asset type | Image |
path | 『.png』背景透明圖檔 |
Trim | 修剪勾yes |
Resize | 100% |
Asset type | 勾Color |
Color | 0xFFFFFF |
Configure Image Asset | Android 7.1以下 |
Icon type | Launcher Icons(Legacy only) |
Name | ic_launcher |
Asset type | 勾Image |
Path | 『.png』背景透明圖檔 |
Trim | 修剪勾yes |
Padding | 0% |
Background | 背景0xFFFFFF |
Scaling | Crop/ Shrink to fit自适应宽度 |
Shape | 揀None |
Effect | 勾None |
資料夾 | Size(px) |
mipmap-xxxhdpi | 192*192 |
mipmap-xxhdpi | 144*144 |
mipmap-xhdpi | 96*96 |
mipmap-hdpi | 72*72 |
mipmap-mdpi | 48*48 |
Android Studio係右下角彈出警示,『Cannot load settings from file』冇法載入『misc.xml』,事實係文檔損毁.內容大致如下.粘貼後諸存.
『misc.xml』文檔 |
<?xml version=”1.0″ encoding=”UTF-8″?>
<project version=”4″> <component name=”ProjectRootManager” version=”2″ languageLevel=”JDK_17″ default=”true” project-jdk-name=”Android Studio default JDK” project-jdk-type=”JavaSDK”> <output url=”file://$PROJECT_DIR$/build/classes” /> </component> <component name=”ProjectType”> <option name=”id” value=”Android” /> </component> </project> |
Load settings |
Cannot load settings from file |
“D:\ANDROID\GeomanticCompass\.idea\misc.xml”;illegal character(NULL,Unicode 0) encountered: not valid in any content at[row,col{unknown-source}]:[1,2] Please correct the file content |
『Android studio』冇法執行『Clang-Tidy』:事因稳吾到或冇法執行『clazy-standalone』.
Unable to execute Clang-Tidy: clazy-standalone is not found or cannot be executed |
事因『Android Studio吾支持『clazy』
TureNAS-內建支援FTP,即使出到街也可係『手機』同『NAS』傳蒞傳去.
ftp://username:password@truenas/ |
Windows10
Android
Android Studio冇端端彈出[CXX5202] 錯誤碼,android程式僅僅支持32BIT/x86庫. 而Google Play自2019年8月1日起, 要支持64Bit庫,
<uses-sdk android:minSdkVersion=”9″ android:targetSdkVersion=”31″ /> |
[CXX5202] This app only has 32-bit [armeabi-v7a,x86] native libraries. Beginning August 1, 2019 Google Play store requires that all apps that include native libraries must provide 64-bit versions. For more information, visit https://g.co/64-bit-requirement |
『西曆』 即『儒略曆』, 電腦內置硬件時鐘,得以存儲時間.
time_t _time; |
time(&_time); |
tm * _tm; |
_tm = localtime(&_time); |
tm將『本地時鐘』分解為『年』『月』『日』『時』『分』『秒』『禮拜』.
struct tm{ | 簡述 |
int tm_year; | 1900 + 年 |
int tm_mon; | 月 0~11 |
int tm_mday; | 日 1~31 |
int tm_hour; | 時 0~23 |
int tm_min | 分 Minute 0~59 |
int tm_sec; | 秒 second 0~60, 60為潤秒,天文曆校准. |
int tm_wday; | 禮拜, 0=禮拜日,1=禮拜壹,2=禮拜貳,3=禮拜三,4=禮拜肆,5=禮拜伍,6=禮拜陸 |
int tm_yday; | 年日,(0~365),壹月壹=0,拾貳月參拾=365 |
int tm_isdst;
} |
夏令时 Daylight saving time,
DST=0冇夏令时, DST>0有夏令时, DST<0夏令时冇效 |
Android触摸Touch 手指触摸荧屏傳遞『鬆UP』『撳DOWN』『拖DRAGGED』基礎信息, 要模拟『單擊』『雙擊』, 首先定義信息指令.
#define TOUCH_CLICK 4 | 單擊 |
#define TOUCH_DOUBLECLICK 5 | 雙擊 |
定義『單擊』『雙擊』時間間隔. 约摸0.1秒即触發單擊,
#define TOUCH_TIME_CLICK 0.5f | 單擊 500毫秒 |
#define TOUCH_TIME_DOUBLECLICK 0.5f | 雙擊 500毫秒 |
定義冚域變量,記录『撳up』『單擊click』触發時間.
double touch_time_down; | 撳-进程時鐘 |
double touch_time_click; | 單擊-进程時鐘 |
获得進程時鐘信号
clock_t clock(void); |
每秒時鐘計時单位
#define CLOCKS_PER_SEC 1000000 | 時鐘单位 |
計進程時間,單位『秒』
『進程時間=時鐘信号/时鐘单位』
double currTime = clock() / CLOCKS_PER_SEC ; |
當接收『鬆UP』指令.
if(action == TOUCH_UP){//鬆 |
判斷两次單擊小於0.5秒触發雙擊
if((currTime – touch_time_click) < TOUCH_TIME_DOUBLECLICK ) { |
touch->action[finger] = TOUCH_DOUBLECLICK; //細過500ms 雙擊 |
touch_time_click = currTime; } |
判斷『鬆UP』『撳DOWN』小於0.5秒触發單擊
else if((currTime – touch_time_down) < TOUCH_TIME_CLICK ) {// 細過500ms |
touch->action[finger] = TOUCH_CLICK; //單擊 |
touch_time_click = currTime; }} |
記录『撳DOWN』
if(action == TOUCH_DOWN) |
touch_time_down = currTime;//撳-进程時間 |
係『LGE Nexus 5』行Debg,報『The application could not be installed: INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES』簽名衝突未能裝APK程式.
事因先前重装『Android Studio』, ADB Debug簽名改變造成.手動卸載舊版搞掂.
07/12 20:16:23: Launching ‘app’ on LGE Nexus 5. |
Installation did not succeed. |
The application could not be installed: INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES |
List of apks:
[0] ‘D:\ANDROID\GeomanticCompass\app\build\intermediates\apk\debug\app-debug.apk’ |
Installation failed due to: ‘Failed to commit install session 1496268207 with command pm install-commit 1496268207. Error: INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: New package has a different signature: net.bookcard.geomanticcompass’ |
Retry |
Failed to launch an application on all devices |
重装Android Studio後,舊時C/C++代碼超出(500kB)限制.代碼解析失效.
File size(698.07kB) exceeds configured limit for C/C++ (500kB).Code insight features are not available. |
撳『Change the maximun file length』更改限制長度. 係右上角
Maximun File Length |
Enter the new maximun length for C/C++ files in characters |
重装Android Studio後Build, 彈出CMake ERROR, 事源CMake未裝
[CXX1300] CMake ‘3.10.2’ was not found in SDK, PATH, or by cmake.dir property. |
[CXX1301] – CMake ‘3.18.1’ found in SDK did not satisfy requested version. |
2D游戲動画幀, 由多幅關鍵幀(keyFrame) 組成. 逐幀渲染產生動画卡通.『幀動画』亦哎呌『紋理』texture. 将多幅『幀動画』存放係單壹紋理.OpenGL可提高渲染速度, 係显存記憶體1MB年代可能冇用. 事因渲染皆係電脑記憶體運作.
係显存記憶體大幅增長, OpenGL渲染係显存記憶體完成. 减小『紋理』載入量, 單壹紋理存放多幅『幀動画』,可提高渲染速度.
係3D游戲亦可用相同技術提飛渲染效率.
void zoom_textcoord_model3D(TEXTURE_PTR texture,int index,int size,VECTOR2D_PTR dest,VECTOR2D_PTR sour,int count,int flag) |
texture | 紋理 |
index | 關鍵幀索引 |
size | 關鍵幀寬高解像 |
dest | 纹理隊列 |
sour | 原始纹理隊列 |
count | 纹理頂點量 |
計單元格
int cell = texture->width / size; |
計索引
int i = index % cell;// 橫索引 |
int j = index / cell;// 行索引 |
計缩放率0.0f < zoom < 1.0f
float zoom = (float)size / (float)texture->width; |
計『幀』偏移.
float offsetX = (float)zoom * (float)i; |
float offsetY = (float)zoom * (float)j; |
遍歴UV紋理頂㸃
for (int p = 0; p < count; ++p){ |
缩細後移動
dest[p].u = sour[p].u * zoom + offsetX ; |
dest[p].v = sour[p].v * zoom + offsetY; |
纹理返转
if (flag & MODEL3D_TEXTURE_FLIP)
dest[p].v = 1 – (sour[p].v * zoom + offsetY);} |
『天幕SKYBOX』指巨立方體, 係內籠貼天幕紋理,『天幕紋理』可能係『地平線』『室內』『宇宙』. 『天幕SKYBOX』原㸃與3D相機位置重合. 係遠睇時正确.
由陸幅紋理『顶』『底』『前』『後』『左』『右』組成. 以前『天幕SKYBOX』紋理分陸幅位圖存檔.
陸幅位圖存係單壹『紋理』效率更高. 将『紋理』平分拾陸等分. 足够擺两組『天幕』紋理. 『日頭』『晚黑』各壹. 似上圖咁.
紋理索引:0~15 | 『日頭』 | 『晚黑』 |
顶up | 0 | 8 |
底dn | 1 | 9 |
前ft | 7 | 15 |
后bk | 5 | 13 |
左lt | 6 | 14 |
右rt | 4 | 12 |
定義『天幕SKYBOX』
typedef struct SKYBOX_TYP { | |
VECTOR3D pos; | 位置 |
VECTOR3D rot; | 旋轉 |
TEXTURE_PTR texture; | 天幕纹理 |
float size; | 天幕大细 |
//TEXTURE_REGION region[16]; | 纹理区域 |
VECTOR3D vertex_array[36] ; | 天幕顶点 |
VECTOR2D texCoord_array[36] ; | 天幕紋理 |
}SKYBOX, *SKYBOX_PTR; |
手エ构建『天幕SKYBOX』立方體『3D頂㸃』同『UV紋理』. 正方形以两三角形組成. 紋理左上角[u0, v0], 紋理右下角[u1,v1].天幕大细『size』.
天『UV紋理』 | 『xyz頂㸃』 |
texCoord[0]=[u1, v1] | vertex_array[0]=[-size, size, -size] |
texCoord[1]=[u0, v1] | vertex_array[1]=[size, size, -size] |
texCoord[2]=[u0, v0] | vertex_array[2]=[size, size, size] |
texCoord[3]=[u1, v1] | vertex_array[3]=[-size, size, -size] |
texCoord[4]=[u0, v0] | vertex_array[4]=[size, size, size] |
texCoord[5]=[u1, v0] | vertex_array[5]=[-size, size, size] |
地『UV紋理』 | 『xyz頂㸃』 |
texCoord[6]=[u1, v1] | vertex_array[6]=[size, -size, -size] |
texCoord[7]=[u0, v1] | vertex_array[7]=[-size, -size, -size] |
texCoord[8]=[u0, v0] | vertex_array[8]=[-size, -size, size] |
texCoord[9]=[u1, v1] | vertex_array[9]=[size, -size, -size] |
texCoord[10]=[u0, v0] | vertex_array[10]=[-size, -size, size] |
texCoord[11]=[u1, v0] | vertex_array[11]=[size, -size, size] |
前『UV紋理』 | 『xyz頂㸃』 |
texCoord[12]=[u0, v0] | vertex_array[12]=[-size, -size, -size] |
texCoord[13]=[u1, v0] | vertex_array[13]=[size, -size, -size] |
texCoord[14]=[u1, v1] | vertex_array[14]=[size, size, -size] |
texCoord[15]=[u0, v0] | vertex_array[15]=[-size, -size, -size] |
texCoord[16]=[u1, v1] | vertex_array[16]=[size, size, -size] |
texCoord[17]=[u0, v1] | vertex_array[17]=[-size, size, -size] |
后『UV紋理』 | 『xyz頂㸃』 |
texCoord[18]=[u0, v0] | vertex_array[18]=[size, -size, size] |
texCoord[19]=[u1, v0] | vertex_array[19]=[-size, -size, size] |
texCoord[20]=[u1, v1] | vertex_array[20]=[-size, size, size] |
texCoord[21]=[u0, v0] | vertex_array[21]=[size, -size, size] |
texCoord[22]=[u1, v1] | vertex_array[22]=[-size, size, size] |
texCoord[23]=[u0, v1] | vertex_array[23]=[size, size, size] |
右『UV紋理』 | 『xyz頂㸃』 |
texCoord[24]=[u1, v0] | vertex_array[24]=[size, -size, size] |
texCoord[25]=[u1, v1] | vertex_array[25]=[size, size, size] |
texCoord[26]=[u0, v1] | vertex_array[26]=[size, size, -size] |
texCoord[27]=[u1, v0] | vertex_array[27]=[size, -size, size] |
texCoord[28]=[u0, v1] | vertex_array[28]=[size, size, -size] |
texCoord[29]=[u0, v0] | vertex_array[29]=[size, -size, -size] |
左『UV紋理』 | 『xyz頂㸃』 |
texCoord[30]=[u1, v0] | vertex_array[30]=[-size, -size, -size] |
texCoord[31]=[u1, v1] | vertex_array[31]=[-size, size, -size] |
texCoord[32]=[u0, v1] | vertex_array[32]=[-size, size, size] |
texCoord[33]=[u1, v0] | vertex_array[33]=[-size, -size, -size] |
texCoord[34]=[u0, v1] | vertex_array[34]=[-size, size, size] |
texCoord[35]=[u0, v0] | vertex_array[35]=[-size, -size, size] |
同Android Studio更新Android SDK後, 最低支持Gradle版本7.3.3, 當前版本7.0.2
Minimum supported Gradle version is 7.3.3. Current version is 7.0.2. |
Please fix the project’s Gradle settings.
Gradle Settings. |
撳『Android Gradle Plugin can be upgraded』更新Gradle
Upgrade Android Gradle Plungin from version 7.0.2 to 7.2.0 |
下載Gradle7.3.3
Gradle: Download gradle-7.3.3-bin.zip |
之前做Android 游戲皆单㸃触摸, 諗住係風水羅盤實現两指縮放.
触摸分叁動作『鬆』『撳』『拖』.
動作 | 注释 |
#define TOUCH_UP 1 | 鬆 |
#define TOUCH_DOWN 2 | 撳 |
#define TOUCH_DRAGGED 3 | 拖 |
雙手拾指, 除非用埋脚指
#define MAX_FINGER 10 | 拾指 |
定義TOUCH結构, 用蒞存拾指『方位』同『動作』
TOUCH結构 | 注释 |
typedef struct TOUCH_STR{ | |
int count; | 手指量, 最多10指 |
int action[MAX_FINGER]; | 動作 |
int x[MAX_FINGER]; | X座標 |
int y[MAX_FINGER]; | Y座標 |
}TOUCH,*TOUCH_PTR; |
用栈stack蒞存触摸,
#define MAX_TOUCH 32 | 棧高32 |
TOUCH touch_array[MAX_TOUCH] ; | 触摸棧 |
int touch_count; | 棧頂 |
棧頂加壹, 每次存触摸『方位』同『動作』前調用
int Add_Touch(){
TOUCH_PTR touch; int index; if(touch_count >= MAX_TOUCH) return touch_count; index = touch_count; ++touch_count;// 棧頂加壹 touch = &touch_array[index]; touch->count = 0; return touch_count; } |
棧頂减壹, 讀『方位』同『動作』後調用
int Sub_Touch(){
if(touch_count <= 0) return touch_count; –touch_count; return touch_count; } |
存触摸『方位』同『動作』, finger係手指索引
bool Set_Touch(int finger,int action,float x,float y){
int index; TOUCH_PTR touch; index = touch_count-1; // 棧頂 touch = &touch_array[index]; if(touch->count < finger + 1) touch->count = finger + 1; touch->action[finger] = action; touch->x[finger] = x ; touch->y[finger] = y ; return true; } |
讀触摸, 『方位』同『動作』, finger係手指索引
bool Get_Touch(int finger,int * action,int * x,int * y){
TOUCH_PTR touch; int index; if(touch_count == 0) return false; index = touch_count – 1; touch = &touch_array[index]; *action = touch->action[finger]; *x = touch->x[finger]; *y = touch->y[finger]; return true; } |
計两指(x0,y0)(x1,y1)縮放時中心位(cx,cy)
float cx = (x0 – x1)/2 + x1; | |
float cy = (y0 – y1)/2 + y1; |
2D触摸坐标转屏幕坐标
VECTOR2D touchPoint2D; | 2D触摸點(x,y) |
VECTOR3D touchPoint3D; | 3D触摸點(x,y,z) |
触摸坐标转3D世界坐标
Init_VECTOR2D(&touchPoint2D, cx, cy); |
TouchToWorld(camera3D, &touchPoint2D, &touchPoint3D); |
計两指距,
Init_VECTOR2D(&v0,x0,y0); |
Init_VECTOR2D(&v1,x1,y1); |
Sub_VECTOR2D(&vdiff,&v0,&v1); |
length = Length_VECTOR2D(&vdiff); |
指距拉開放大, 两指行埋縮細. 通過移3D相機實現縮放.
if(_length > length ) y = Camera3D.pos.y + 2; | 縮細 |
if(_length < length ) y = Camera3D.pos.y – 2; | 放大 |
自首台Android手機面世, 已標配熒幕觸摸. 旋轉羅盤以天池為原㸃分肆象限.
sin(a)= 對邊/斜邊 |
a = asinf(對邊/斜邊) |
angle = RAD_TO_DEG( asinf((length / 2.0f) / 1.0f) ) * 2.0f; |
笛卡兒坐標 | 象限 |
if( v0.y > 0 && v0.x > _v0.x) length = -length; | 逆轉 |
if( v0.y < 0 && v0.x < _v0.x)length = -length; | 逆轉 |
rot.y = (int)(rot.y + angle) % 360; |
琴日Android Studio係Build果陣,Win10冇端端死機, 重啟後『Clean Project』『Rebuild Project』皆現『app:externalNativeBuildCleanDebug FAILED』
> Task :app:externalNativeBuildCleanDebug FAILED
Execution failed for task ‘:app:externalNativeBuildCleanDebug’. > com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $
* Try: Run with –stacktrace option to get the stack trace. Run with –info or –debug option to get more log output. Run with –scan to get full insights. |
Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $ |
問題明显,係寫入時果陣死機. 破壞文档数据, 解决方案係刪『build』資料夾, 再撳『Rebuild Project』 等『Androd studio』重建『build』即可
1.刪『App』->『build』資料夾 |
2.撳『build』->『Rebuild Project』 |
『assets讀文檔』, 如果有大量文檔,要逐壹畀文檔路徑蒞讀, 更佳係『assets』 裏邊.
將『文檔』擺係『檔案夾』. 然後遍歷『文檔』.
訪問『assets』檔案夾
AAssetDir * assetDir; |
開啟文檔夾, 『dir』係『assets』 裏邊
assetDir = AAssetManager_openDir(assetManager,dir); |
失敗返回NULL
if(assetDir == NULL) return false; |
遍歷文檔,
while((filename = AAssetDir_getNextFileName(assetDir)) != NULL) { |
組合『assets』『文檔』路徑.
sprintf(path, “%s/%s”, dir, filename); |
閂檔案夾
AAssetDir_close(assetDir); |
『觸屏坐標』『x,y』坐標轉『正交投影』坐標, 『視錐體解像』寬高, 比例需手機解像寬高比壹致.
計屏幕寬高比
float aspect_ratio = (float)cam->real_width / (float)cam->real_height; |
『視錐體解像』寬高,此時定義『高』800pix
float frustum_width = 800 *aspect_ratio; |
float frustum_height = 800 ; |
『正交投影』代碼
重置視區尺寸, 值係手機解像寬高
::glViewport(0,0,real_width,real_height); |
設定投影矩陣
::glMatrixMode(GL_PROJECTION); |
載入單位矩陣
::glLoadIdentity(); |
正交投影, 游戲坐標原點(0,0,0)為於屏幕中心
glOrthof(frustum_width / 2, frustum_width / 2, -frustum_height / 2, frustum_height / 2, pos.y – 10, far_clip_z); |
設定模型視圖矩陣
::glMatrixMode(GL_MODELVIEW); |
載入單位矩陣
::glLoadIdentity(); |
手指触摸手機屏幕onTouch() 所得坐標需轉游戲世界坐標,正交投影OpenGL游㱆+Z軸指向屏幕深處.
float touch3Dx = (touch2Dx / real_width) * frustum_width ; |
float touch3Dz = (touch2Dy /real_height) * frustum_height ; |
計3D相機位置
touch3Dx = touch3Dx + camPosX; |
touch3Dz = touch3Dz + camPosX; |
游戲坐標原點(0,0,0)為於屏幕中心
touch3Dx = touch3Dx – (frustum_width / 2.0f); |
touch3Dz = touch3Dz – (frustum_height / 2.0f); |
『漢字字庫』同 『ASCII字庫』原理同, 字庫『竪排』, 漢字『32*32』pixel, 『竪』32漢字.
由上至下,由右至左排列.可填1024字符,每色8Bit. 即『索引色』『調色板』.
准備庫位圖
Photoshop轉為『索引色』
止時圖檔『調色板』共有三色『黑』『白』『透明』.
IMAGE-SIZE | 1024*1024 |
FONT-SIZE | 30pt |
FONT | 衡山毛筆フォント |
FONT-PIXEL | 32pixel*32pixel |
影像-模色 | 索引色 |
色盤 | 正確 |
顏色 | 3 |
强制 | 黑白 |
透明 | 勾選 |
『調色板』結构同DirextX唔同, 將flags存alpha『透明值』0~255,0係『透明』,255係『實心』
typedef struct PALETTE_TYP {
BYTE red; BYTE green; BYTE blue; BYTE flags;//alpha } PALETTE,COLOR,* PALETTE_PTR,*COLOR_PTR; |
設置『調色板』顏色
#define INIT_PALETTE(palette,r,g,b,a) {(palette).red=(r); (palette).green=(g); (palette).blue=(b); (palette).flags=(a);} |
黑字『調色板』設置
index | red | green | blue | Alpha |
253 | 0xff | 0xff | 0xff | 0x00 |
255 | 0x00 | 0x00 | 0x00 | 0xFF*0.5f |
白字『調色板』設置
index | red | green | blue | Alpha |
253 | 0x00 | 0x00 | 0x00 | 0x00 |
255 | 0xff | 0xff | 0xff | 0xFF*0.5f |
半透明,激活混合
glEnable(GL_BLEND); |
設混合模式, 渲染時Alpha值混合.
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
激活透明测试
glEnable(GL_ALPHA_TEST); |
Alpha=0時, 過濾背影色
glAlphaFunc(GL_GREATER, 0); |
買Google nexus5『壹為神功,貳為弟子』,『壹蒞開發,貳還心愿』.機仔細細窄邊框.當年靚絕Android, 本蒞愛『Android 4』『黑蓋』寄蒞『Android 5』『白蓋』. 韓國LG制造港版,換新電包郵百肆蚊有找.部機吾升級係用吾到, 永遠話連吾到.
Google nexus5 | 簡介 |
屏 | 4.94英寸 |
解像 | 1920*1080 |
電 | 4000mAh |
相機 | 1310萬 |
CPU | 驍龍800 |
RAM | 2GB |
DISK | 32GB |
MODEL | LG-D821港版 |
初台Android手機己采用『單點』触摸, 直至Android2.0(SDK version 5) 先支持『多點』触摸.
static class TounchListener implements OnTouchListener{ |
@Override
public boolean onTouch(View v, MotionEvent event) { |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) |
MultiTouch(v, event); else |
SingleTouch(v, event); |
return false;} |
private static TounchListener Tounch_Listener = new TounchListener() ; |
view.setOnTouchListener(Tounch_Listener); |
MotionEvent.getX() | X軸指右 |
MotionEvent.getY() | Y軸指下 |
MotionEvent.getAction() | |
MotionEvent.ACTION_DOWN | 手指撳屏 |
MotionEvent.ACTION_POINTER_DOWN | 手指撳屏 |
MotionEvent.ACTION_UP | 手指鬆离 |
MotionEvent.ACTION_POINTER_UP | 手指鬆离 |
MotionEvent.ACTION_CANCEL | 手勢鬆 |
MotionEvent.ACTION_MOVE | 移動手指 |
public static int ACTION_UP = 1; | 鬆 |
public static int ACTION_DOWN = 2; | 撳 |
public static int ACTION_DRAGGED = 3; | 拖 |
處理『單點』触摸
static void SingleTouch(View v, MotionEvent event){ |
int action = event.getAction() & MotionEvent.ACTION_MASK; |
if(action == MotionEvent.ACTION_DOWN ||
action == MotionEvent.ACTION_POINTER_DOWN) |
Lib.setTouch(ACTION_DOWN,event.getX(),event.getY());else |
if(action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_CANCEL) |
Lib.setTouch(ACTION_UP,event.getX(),event.getY());else |
if(action == MotionEvent.ACTION_MOVE) |
Lib.setTouch(ACTION_DRAGGED,event.getX(),event.getY());} |
處理『多點』触摸
int Pointer_Index = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK)>>MotionEvent.ACTION_POINTER_ID_SHIFT; |
MotionEvent.getX(index) | X軸指右 |
MotionEvent.getY(index) | Y軸指下 |
int action = event.getAction() & MotionEvent.ACTION_MASK; |
int Pointer_Index = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK)>>MotionEvent.ACTION_POINTER_ID_SHIFT; |
int Pointer_Count = event.getPointerCount(); |
for(int i=0; i< Pointer_Count; ++i) { |
if (action != MotionEvent.ACTION_MOVE && i != Pointer_Index)
continue; |
if(action == MotionEvent.ACTION_DOWN ||
action == MotionEvent.ACTION_POINTER_DOWN) |
Lib.setTouch(ACTION_DOWN,event.getX(i),event.getY(i));else |
if(action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_CANCEL) |
Lib.setTouch(ACTION_UP,event.getX(i),event.getY(i));else |
if(action == MotionEvent.ACTION_MOVE) |
Lib.setTouch(ACTION_DRAGGED,event.getX(i),event.getY(i));} |
係C++緩存『触屏』信息Lib.setTouch();
typedef struct TOUCH_STR{ | |
int action; | 触屏類型 |
int x,y; | 触屏坐標 |
}TOUCH,*TOUCH_PTR; |
『触屏座標』轉『熒屏座標』
void TouchToScreen(CAMERA2D_PTR cam,VECTOR2D_PTR touch){ |
touch->x = (touch->x / (float)cam->real_width) * cam->frustum_width * cam->zoom; |
touch->y = (1-touch->y /(float)cam->real_height) * cam->frustum_height * cam->zoom; } |
『Canon PRINT Business』運作需裝『Canon Print Service』,可直接發指今掃描影印『文檔』『圖檔』.
『Mopria Paint』係Android手機平板發影印指令. 如果『Mopria Paint』報『ERROR CODE 853』, 可嘗試『Canon Print Service』,Canon專用Android影印機程式. 同『Mopria Paint』係『外掛程式』後台臺運行, 向Canon發影印指令.
份數 | 1 |
紙張尺碼 | ISO A4 |
色彩 | 黑色/彩色 |
方向 | 縱向/橫向 |
雙面 | 無/長邊/短邊 |
全屏令人沉浸游戲免受干擾. 收埋頂『狀態』『標題』底『導航』.
requestWindowFeature(Window.FEATURE_NO_TITLE); |
Window window = getWindow(); |
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN); |
setContentView(R.layout.activity_main); |
<style name=”Theme.Fullscreen” parent=”android:Theme.NoTitleBar.Fullscreen” /> |
<application android:theme=”@style/Theme.Fullscreen” > |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) |
View decorView = window.getDecorView(); |
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN); |
setSystemUiVisibility() | 簡述 |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | 飛底『導航』 |
View.SYSTEM_UI_FLAG_FULLSCREEN | 飛頂『狀態』 |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | 自動恢復『沉㓎模式』 |
View.SYSTEM_UI_FLAG_IMMERSIVE | 『沉㓎模式』 |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { |
WindowManager.LayoutParams param = window.getAttributes(); |
param.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; |
window.setAttributes(param);} |
layoutInDisplayCutoutMode | 簡述 |
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT | 全屏唔延伸至『兔唇 』, 非全屏延伸至『兔唇 』 |
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER | WINDOW唔延伸至『兔唇 』 |
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES | 窻口延伸至『兔唇 』 |
Android 螢幕耗電, 擺低手機螢幕變暗,幷鎖定. 好似你唔惏野會放蚊咁.用『喚醒鎖』WakLock. 保持清醒『螢幕喚醒』.
<uses-permission android:name=”android.permission.WAKE_LOCK” /> |
PowerManager powerManager; |
powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); |
PowerManager.WakeLock wakeLock; |
wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, “WAKE LOCK”); |
wakeLock.acquire(); |
wakeLock.release(); |
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); |
係游戲渲染文本,『點陣字體』係遠古技術, 係位圖繪畫ASCII字符, 哎『位圖字符glyph』,標准ASCII字符128,有96拉丁字母可渲染,從32到126.『32』係『吉格』.
位圖每行16字符. 位圖寬高符合2n.位圖『256256』每字符『1616』像素, 位圖『512512』每字符『3232』像素.
定義位圖字符
typedef struct FONT_TYP{ | |
TEXTURE_PTR texture; | 字庫紋理 |
DWORD flag; | 頂點標記 |
TEXTURE_REGION region[96]; | 紋理區域 |
}FONT, * FONT_PTR; |
for(int index = 0; index < 96; ++index){ |
Init_Region_Texture(&font->region[index],font->texture,x,y, width, height); |
x = x + width; |
if(x >= offsetX + width * row){
x = offsetX; y = y + height;} |
渲染位圖字庫
VECTOR2D vertex_array[6 * 1024] ; |
VECTOR2D texCoord_array[6 * 1024] ; |
glEnable(GL_TEXTURE_2D); |
glDisable(GL_NORMALIZE); |
glEnable(GL_ALPHA_TEST); |
glAlphaFunc(GL_GREATER, 0); |
TEXTURE_PTR texture = font->texture;
glBindTexture(GL_TEXTURE_2D, texture->ID); |
glEnableClientState(GL_VERTEX_ARRAY); |
glEnableClientState(GL_TEXTURE_COORD_ARRAY); |
因為OpenGL『2D相機』原點『0,0』係『左下角』, 返䡛Y軸.
y = Font_frustum_height – y – height; |
for(int index = 0; index < length; ++index ){ |
int c = text[index] – ‘ ‘; |
if(c < 0 || c > 96) continue; |
TEXTURE_REGION_PTR region = &font->region[c]; |
Init_VECTOR2D(&vertex_array[0 * 3 + 0], x, y + height);
Init_VECTOR2D(&vertex_array[0 * 3 + 1], x + width, y + height); Init_VECTOR2D(&vertex_array[0 * 3 + 2], x + width, y); Init_VECTOR2D(&vertex_array[1 * 3 + 0], x, y + height); Init_VECTOR2D(&vertex_array[1 * 3 + 1], x + width, y); Init_VECTOR2D(&vertex_array[1 * 3 + 2], x, y); |
Init_VECTOR2D(&texCoord_array[0 * 3 + 0], region->u1, region->v2);
Init_VECTOR2D(&texCoord_array[0 * 3 + 1], region->u2, region->v2); Init_VECTOR2D(&texCoord_array[0 * 3 + 2], region->u2, region->v1); Init_VECTOR2D(&texCoord_array[1 * 3 + 0], region->u1, region->v2); Init_VECTOR2D(&texCoord_array[1 * 3 + 1], region->u2, region->v1); Init_VECTOR2D(&texCoord_array[1 * 3 + 2], region->u1, region->v1); |
glVertexPointer(2, GL_FLOAT, 0, vertex_array); |
glTexCoordPointer(2, GL_FLOAT, 0, texCoord_array); |
glDrawArrays(GL_TRIANGLES, 0, 2 * 3 ); |
if(VH == FONT_HORIZONTAL)
x = x + width; else if(VH == FONT_VERTICAL) y = y + height;} |
glDisable(GL_ALPHA_TEST); |
glPopMatrix(); |
ASCII數字 | 字符 |
0–31 | 控制字元,用于控制印表機等周邊設備 |
32-126 | 鍵盤字符 |
127 | DELETE 命令 |
48-57 | 0-9 |
65-90 | A-Z |
97-122 | a-z |
128-255 | 擴展ASCII,增加特殊符號字符, 歐語字母和圖形符號 |
Photoshop位圖字符glyph | |
Pixel | 32*32 |
Font | Terminal Regular |
Size | 4pt |
mid | |
Aa |
十進制(DEC) | 十六進制(HEX) | ASCII字符 | 簡介 |
0 | 0x00 | 空格NUL(null) | |
1 | 0x01 | 標題開始 SOH(start of headling) | |
2 | 0x02 | 正文開始 STX (start of text) | |
3 | 0x03 | 正文結束 ETX (end of text) | |
4 | 0x04 | 傳輸結束 EOT (end of transmission) | |
5 | 0x05 | 請求 ENQ (enquiry) | |
6 | 0x06 | 收到通知 ACK (acknowledge) | |
7 | 0x07 | 響鈴 BEL (bell) | |
8 | 0x08 | 退格 BS (backspace) | |
9 | 0x09 | 水準跳位字元 HT (horizontal tab) | |
10 | 0x0A | 換行鍵 LF (NL line feed, new line) | |
11 | 0x0B | 垂直跳位字元 VT (vertical tab) | |
12 | 0x0C | 換頁鍵 FF (NP form feed, new page) | |
13 | 0x0D | 回車鍵 CR (carriage return) | |
14 | 0x0E | 不用切換 SO (shift out) | |
15 | 0x0F | 啟用切換 SI (shift in) | |
16 | 0x10 | 資料連結轉義DLE (data link escape) | |
17 | 0x11 | 設備控制1 DC1 (device control 1) | |
18 | 0x12 | 設備控制2 DC2 (device control 2) | |
19 | 0x13 | 設備控制3 DC3 (device control 3) | |
20 | 0x14 | 設備控制4 DC4 (device control 4) | |
21 | 0x15 | 拒絕接收 NAK (negative acknowledge) | |
22 | 0x16 | 同步空閒 SYN (synchronous idle) | |
23 | 0x17 | 傳輸塊結束 ETB (end of trans. block) | |
24 | 0x18 | 取消 CAN (cancel) | |
25 | 0x19 | EM (end of medium) | |
26 | 0x1A | 替補 SUB (substitute) | |
27 | 0x1B | 溢出 ESC (escape) | |
28 | 0x1C | 檔分割符 FS (file separator) | |
29 | 0x1D | 分組符 GS (group separator) | |
30 | 0x1E | 記錄分離符 RS (record separator) | |
31 | 0x1F | 單元分隔符號 US (unit separator) | |
32 | 0x20 | 空格 (space) | |
33 | 0x21 | ! | 嘆號 |
34 | 0x22 | “ | 雙引號 |
35 | 0x23 | # | 井號 |
36 | 0x24 | $ | 美元符 |
37 | 0x25 | % | 百分號 |
38 | 0x26 | & | 和號 |
39 | 0x27 | ‘ | 閉單引號 |
40 | 0x28 | ( | 開括弧 |
41 | 0x29 | ) | 閉括弧 |
42 | 0x2A | * | 星號 |
43 | 0x2B | + | 加號 |
44 | 0x2C | , | 逗號 |
45 | 0x2D | – | 減號/破折號 |
46 | 0x2E | . | 句號 |
47 | 0x2F | / | 斜杠 |
48 | 0x30 | 0 | |
49 | 0x31 | 1 | |
50 | 0x32 | 2 | |
51 | 0x33 | 3 | |
52 | 0x34 | 4 | |
53 | 0x35 | 5 | |
54 | 0x36 | 6 | |
55 | 0x37 | 7 | |
56 | 0x38 | 8 | |
57 | 0x39 | 9 | |
58 | 0x3A | : | 冒號 |
59 | 0x3B | ; | 分號 |
60 | 0x3C | < | 小於 |
61 | 0x3D | = | 等號 |
62 | 0x3E | > | 大於 |
63 | 0x3F | ? | 問號 |
64 | 0x40 | @ | 電子郵件符號 |
65 | 0x41 | A | |
66 | 0x42 | B | |
67 | 0x43 | C | |
68 | 0x44 | D | |
69 | 0x45 | E | |
70 | 0x46 | F | |
71 | 0x47 | G | |
72 | 0x48 | H | |
73 | 0x49 | I | |
74 | 0x4A | J | |
75 | 0x4B | K | |
76 | 0x4C | L | |
77 | 0x4D | M | |
78 | 0x4E | N | |
79 | 0x4F | O | |
80 | 0x50 | P | |
81 | 0x51 | Q | |
82 | 0x52 | R | |
83 | 0x53 | S | |
84 | 0x54 | T | |
85 | 0x55 | U | |
86 | 0x56 | V | |
87 | 0x57 | W | |
88 | 0x58 | X | |
89 | 0x59 | Y | |
90 | 0x5A | Z | |
91 | 0x5B | [ | 開方括號 |
92 | 0x5C | \ | 反斜杠 |
93 | 0x5D | ] | 閉方括號 |
94 | 0x5E | ^ | 脫字元 |
95 | 0x5F | _ | 下劃線 |
96 | 0x60 | ` | 開單引號 |
97 | 0x61 | a | |
98 | 0x62 | b | |
99 | 0x63 | c | |
100 | 0x64 | d | |
101 | 0x65 | e | |
102 | 0x66 | f | |
103 | 0x67 | g | |
104 | 0x68 | h | |
105 | 0x69 | i | |
106 | 0x6A | j | |
107 | 0x6B | k | |
108 | 0x6C | l | |
109 | 0x6D | m | |
110 | 0x6E | n | |
111 | 0x6F | o | |
112 | 0x70 | p | |
113 | 0x71 | q | |
114 | 0x72 | r | |
115 | 0x73 | s | |
116 | 0x74 | t | |
117 | 0x75 | u | |
118 | 0x76 | v | |
119 | 0x77 | w | |
120 | 0x78 | x | |
121 | 0x79 | y | |
122 | 0x7A | z | |
123 | 0x7B | { | 開花括弧 |
124 | 0x7C | | | 垂線 |
125 | 0x7D | } | 閉花括弧 |
126 | 0x7E | ~ | 波浪號 |
127 | 0x7F | 刪除DEL(DELETE) | |
128 | 0x80 | Ç | Ccedil |
129 | 0x81 | ü | uuml |
130 | 0x82 | é | eacute |
131 | 0x83 | â | circ |
132 | 0x84 | ä | auml |
133 | 0x85 | à | agrave |
134 | 0x86 | å | aring |
135 | 0x87 | ç | ccedil |
136 | 0x88 | ê | ecirc |
137 | 0x89 | ë | euml |
138 | 0x8A | è | egrave |
139 | 0x8B | ï | iuml |
140 | 0x8C | î | icirc |
141 | 0x8D | ì | igrave |
142 | 0x8E | Ä | Auml |
143 | 0x8F | Å | ring |
144 | 0x90 | É | Eacute |
145 | 0x91 | æ | aelig |
146 | 0x92 | Æ | AElig |
147 | 0x93 | ô | ocirc |
148 | 0x94 | ö | ouml |
149 | 0x95 | ò | ograve |
150 | 0x96 | û | ucirc |
151 | 0x97 | ù | ugrave |
152 | 0x98 | ÿ | yuml |
153 | 0x99 | Ö | Ouml |
154 | 0x9A | Ü | Uuml |
155 | 0x9B | ¢ | 美分(cent) |
156 | 0x9C | £ | 英磅(pound) |
157 | 0x9D | ¥ | 日元(yen) |
158 | 0x9E | ₧ | |
159 | 0x9F | ƒ | |
160 | 0xA0 | á | aacute |
161 | 0xA1 | í | iacute |
162 | 0xA2 | ó | oacute |
163 | 0xA3 | ú | uacute |
164 | 0xA4 | ñ | ntilde |
165 | 0xA5 | Ñ | Ntilde |
166 | 0xA6 | ª | |
167 | 0xA7 | º | |
168 | 0xA8 | ¿ | |
169 | 0xA9 | ⌐ | |
170 | 0xAA | ¬ | |
171 | 0xAB | ½ | |
172 | 0xAC | ¼ | |
173 | 0xAD | ¡ | |
174 | 0xAE | « | |
175 | 0xAF | » | |
176 | 0xB0 | ░ | |
177 | 0xB1 | ▒ | |
178 | 0xB2 | ▓ | |
179 | 0xB3 | │ | |
180 | 0xB4 | ┤ | |
181 | 0xB5 | ╡ | |
182 | 0xB6 | ╢ | |
183 | 0xB7 | ╖ | |
184 | 0xB8 | ╕ | |
185 | 0xB9 | ╣ | |
186 | 0xBA | ║ | |
187 | 0xBB | ╗ | |
188 | 0xBC | ╝ | |
189 | 0xBD | ╜ | |
190 | 0xBE | ╛ | |
191 | 0xBF | ┐ | |
192 | 0xC0 | └ | |
193 | 0xC1 | ┴ | |
194 | 0xC2 | ┬ | |
195 | 0xC3 | ├ | |
196 | 0xC4 | ─ | |
197 | 0xC5 | ┼ | |
198 | 0xC6 | ╞ | |
199 | 0xC7 | ╟ | |
200 | 0xC8 | ╚ | |
201 | 0xC9 | ╔ | |
202 | 0xCA | ╩ | |
203 | 0xCB | ╦ | |
204 | 0xCC | ╠ | |
205 | 0xCD | ═ | |
206 | 0xCE | ╬ | |
207 | 0xCF | ╧ | |
208 | 0xD0 | ╨ | |
209 | 0xD1 | ╤ | |
210 | 0xD2 | ╥ | |
211 | 0xD3 | ╙ | |
212 | 0xD4 | Ô | |
213 | 0xD5 | ╒ | |
214 | 0xD6 | ╓ | |
215 | 0xD7 | ╫ | |
216 | 0xD8 | ╪ | |
217 | 0xD9 | ┘ | |
218 | 0xDA | ┌ | |
219 | 0xDB | █ | |
220 | 0xDC | ▄ | |
221 | 0xDD | ▌ | |
222 | 0xDE | ▐ | |
223 | 0xDF | ▀ | |
224 | 0xE0 | α | 阿爾法(Alpha) |
225 | 0xE1 | ß | 貝塔(beta) |
226 | 0xE2 | Γ | Gamma |
227 | 0xE3 | π | 圓周率(pi) |
228 | 0xE4 | Σ | sigma |
229 | 0xE5 | σ | sigma |
230 | 0xE6 | µ | mu |
231 | 0xE7 | τ | tau |
232 | 0xE8 | Φ | PHi |
233 | 0xE9 | Θ | Theta |
234 | 0xEA | Ω | 歐米伽(Omega) |
235 | 0xEB | δ | Delta |
236 | 0xEC | ∞ | 無窮 |
237 | 0xED | φ | phi |
238 | 0xEE | ε | epsilon |
239 | 0xEF | ∩ | |
240 | 0xF0 | ≡ | |
241 | 0xF1 | ± | |
242 | 0xF2 | ≥ | |
243 | 0xF3 | ≤ | |
244 | 0xF4 | ⌠ | |
245 | 0xF5 | ⌡ | |
246 | 0xF6 | ÷ | |
247 | 0xF7 | ≈ | |
248 | 0xF8 | ≈ | |
249 | 0xF9 | ∙ | |
250 | 0xFA | · | |
251 | 0xFB | √ | |
252 | 0xFC | ⁿ | |
253 | 0xFD | ² | |
254 | 0xFE | ■ | |
255 | 0xFF | ÿ |
係2D游戲位圖愛蒞做動畫, 要過濾『背景色』, 係Android OpenGL ES用alpha屏蔽『背景色』, 壹法位圖『A分量』Alpha = 0, 貳法黑色定為『背景色』係載入時將『A分量』Alpha = 0. 所以兩法要係『GL_RGBA』模式運運作.
RGBA8888位圖,RGB占24bit, Alpha 值A占8bit, 透明度範圍『0~255』. 『0』通透.『 255』實體.
設Alpha值 | 通透 | 實體 |
glAlphaFunc() | 0.0 | 1.0 |
glAlphaFuncx() | 0 | 255 |
若位圖冇Alpha 值, OpenGL ES 係載入時將Alpha值設1.
BGRA8888/ARGB8888 | Alpha = 0 |
RGB656,RGB888,index | R=0,G=0,B=0, 黑色 |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width , height , 0, GL_RGBA, GL_UNSIGNED_BYTE, image); |
glEnable(GL_ALPHA_TEST); |
glAlphaFunc(GL_GREATER, 0); |
glAlphaFuncx(GL_GREATER, 0); |
OpenGL有『平行投影』同『透視投影』, 2D相機係『平行投影』生成方盒『視體』, 愛蒞剪裁物體, 唔係『視體』內唔『渲染』. 愛蒞『等比例游戲』『平面游戲』
OpenGL『2D相機』原點『0,0』係『左下角』, z軸遠端係負,近端正.
Android『熒屏』原點『0,0』係『左上角』,
glMatrixMode(GL_PROJECTION); |
glLoadIdentity(); |
glViewport(x, y, window_width, window_height); |
glOrthof (left,right,bottom,top,near,far); |
glOrthof (0, frustum_width, 0, frustum_height, 1, -1); |
glOrthof (x- width/2, x+width/x, y-height/2, y+height/2, 1, -1); |
glOrthof() | |
『left, bottom』 | 左下角『0, 0』 |
『right, top』 | 右上角『width, height』 |
near | z軸近端剪裁面 |
far | z軸遠端剪裁面 |
glMatrixMode(GL_MODELVIEW); |
glLoadIdentity(); |
示例
void Projection_Camera2D(CAMERA2D_PTR cam){
// 重置視區尺寸 ::glViewport(0,0,cam->real_width,cam->real_height); // 設爲投影矩陣 ::glMatrixMode(GL_PROJECTION); // 載入單位矩陣 ::glLoadIdentity(); // 正交投影 glOrthof (0, cam->frustum_width, 0, cam->frustum_height, 1, -1); // 設定模型視圖矩陣 ::glMatrixMode(GL_MODELVIEW); // 載入單位矩陣 ::glLoadIdentity(); } |
『JavaVM』指『Java虛擬機』, 壹進程『Process』壹『JavaVM』, 冚辦爛線程『Thread』共用壹『JavaVM』.
係原生代碼獲得『Java虛擬機』, 通過『JNI_OnLoad ()』函式, 載入共享庫『System.loadLibrary(“lib”);』,載入『LIB.SO』 會自動調用『JNI_OnLoad ()』. 將javaVm以全域變量儲存.
緩存JavaVM接口指針
JavaVM javaVm; |
jint JNI_OnLoad (JavaVM* vm, void* reserved){
javaVm = vm; return JNI_VERSION_1_4; } |
加入『jni.h』頭文檔聲明『Java原生接口』
#include <jni.h> |
『JNI』指『Java Native Interface』『Java原生接口』,實現『JavaVM』同『Native』通信.『Java代碼』同『原生代碼』調用.
『JNIEnv』指向當前『線程局部數據』, 『線程局部數據』內含『原生函式指針列表』. 壹線程『thread』壹『JNIEnv』. 唔得跨線程『thread』傳遞彼此獨立,
事因『POSIX線程』唔屬于『JAVA』平臺, 導致『JAVA虛擬機』冇發識別『POSIX線程』, 為咗訪問『JAVA虛擬機』,『POSIX線程』線程先附係『JAVA虛擬機』再行.
if(javaVm->AttachCurrentThread(&env,NULL) == 0) |
if(javaVm->DetachCurrentThread() == 0) |
Android_Studio_NDK_POSIX_Thread
Android Studio NDK-POSIX線程
『Android Studio NDK』生成『C/C++』線程,使用 POSIX線程簡稱『Pthreads』. 儒略歷1995年發布POSIX.1c『標准』『擴展』綫程.為線程定義『創建』『處理』API.『Micrsoft Windows』『Mac OS X』『Linux』冚辬蘭支持POSIX線程.
事因『POSIX線程』唔屬于『JAVA』平臺, 導致『JAVA虛擬機』冇發識別『POSIX線程』, 為咗訪問『JAVA虛擬機』,『POSIX線程』線程先附係『JAVA虛擬機』再行.
#include <pthread.h> // POSIX線程 |
#include <jni.h> |
int pthread_create(pthread_t * thread,
pthread_attr_t const * attr, void *(* start_routine)(void *), void * arg); |
參數 | 簡介 |
thread | 返回新建綫程句柄 |
attr | 指定線程屬性,傅NULL使用默認值. |
start_routine | 指向綫程函式地址, |
args | 線程函式參數 |
0 | 線程啟動 |
Error code | 錯誤碼 |
pthread_t threadID; |
typedef struct OPENGL_TYP{
JNIEnv *env;// JNI AAssetManager * aassetManager;// 資源管理 EGLDisplay display ; EGLSurface surface;// 內核渲染表層 int Width, Height;// 熒屏寬高 }OPENGL,*OPENGL_PTR; |
void * start_rountine (void * args) |
void * run_game(void * param){
while (run == true) { Update_World();// 更新 Draw_World();// 繪畫 } } |
jint JNI_OnLoad (JavaVM* vm, void* reserved){
javaVm = vm; return JNI_VERSION_1_4; } |
if(javaVm->AttachCurrentThread(&env,NULL) == 0) |
if(javaVm->DetachCurrentThread() == 0) |
if( pthread_create(&threadID, NULL, run_game, openGL) == 0)
return true; else return false; |
void * run_game(void * param){
// 將線程附著係JAVA虛擬機, if(javaVm->AttachCurrentThread(&env,NULL) != 0) return NULL; Init_Engine( ); // 初此引擎 Init_World( ); // 初此游戲世界 while (run == true) { Update_World();// 更新 Draw_World();// 繪畫 } Shutdown_World();// 閂游戲世界 Shutdown_Engine(); // 閂引擎 // 係JAVA虛擬機分離綫程 javaVm->DetachCurrentThread(); return NULL; } |
之前係『JAVA Thread』行『ANativeWindow_fromSurface()』冇事,係『C/C++ Thread』行『ANativeWindow_fromSurface()』死機. 暈得壹陣陣.
係『Android OpenGL ES』用『SurfaceView』蒞畀『OpenGL ES』渲染. 之前『SurfaceView』代碼擺係『MainActivity.java』.更佳方法係『SurfaceView』『Runnable』『SurfaceHolder.Callback』封裝成單獨『GameView calss』.以保持代碼『簡潔』同埋『重用』.
『File->New->Java Class』 |
public class GameView extends SurfaceView implements Runnable,SurfaceHolder.Callback{ |
int view_width,view_height;
public SurfaceHolder surfaceHolder; public Surface surface;//用蒞渲染 |
public GameView(Context context) {
super(context); init(context);// 設置渲染 } |
public GameView(Context context, AttributeSet attrs) {
super(context); init(context);// 設置渲染 } |
public GameView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context); init(context);// 設置渲染 } |
public void init(Context context){
this.surfaceHolder = getHolder(); this.surfaceHolder.addCallback(this); this.surface = surfaceHolder.getSurface(); } |
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder){ new Thread(this).start(); } |
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height){ view_width = width; view_height = height; } |
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) { } |
@Override
public void run() { AssetManager assetManager = getContext().getAssets(); Lib.init(assetManager, surface, view_width, view_height); while (true) {// 請求渲染 Lib.update(); } } |
Android Java原生接口(JNI) 令 『Java』 同『C\C++』代碼相互調用. 之前係『MainActivity.java』声明『native』原生代碼接口.
static public native String stringFromJNI(); |
『native-lib.cpp』『C\C++』代碼, 它編譯共享庫『.so』檔
extern “C” JNIEXPORT jstring JNICALL
Java_net_bookcard_compass_MainActivity_stringFromJNI(JNIEnv* env,jobject this) { return env->NewStringUTF(“native-lib”) ; } |
缺點係只能『MainActivity.java』調用, 更佳方發係將原生代碼接口擺係單獨『Java Class』. 并声明為『static』, 可係任意位置調用『C\C++』代碼.
『File』->『New』->『Java Class』 |
static {
System.loadLibrary(“geomanticcompass”); } |
static public native String stringFromJNI(); |
『GPS』全球定位代碼,係『eclipse』移過蒞『Android Studio』, 發現getBestProvider()返回NULL. 源於冇『權限』 , 而『Android 6』後要人手獲得授權, 冇『權限』getBestProvider()永遠返回null. 係程式啟動檢查『權限』, 若冇『權限』提出請求伸請.
string GetBestProvider (Criteria criteria, bool enabledOnly) |
『GPS』定位伸請『權限』
<uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION”/> |
<uses-permission android:name=”android.permission.ACCESS_COARSE_LOCATION”/> |
『權限』
Manifest.permission.ACCESS_FINE_LOCATION | 高精度定位 |
Manifest.permission.ACCESS_COARSE_LOCATION | 低精度定位 |
檢查『權限』
int coarse = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION); |
int fine = ContextCompat.checkSelfPermission(context,Manifest.permission.ACCESS_FINE_LOCATION); |
返回值判斷係咪獲得『權限』
PackageManager.PERMISSION_GRANTED | 有『權限』 |
PackageManager.PERMISSION_DENIED | 冇『權限』 |
判斷有冇『權限』
if(coarse == PackageManager.PERMISSION_DENIED || fine == PackageManager.PERMISSION_DENIED ) |
請求伸請. context必需係Activity
String permission[] = new String[] { Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION}; |
ActivityCompat.requestPermissions((Activity) context,permission,REQUEST_CODE_GPS); |
GPS權限請求碼
public static int REQUEST_CODE_GPS = 156; |
處理GPS授權回調, 係MainActivity判斷授權
@Override
public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults){ |
requestCode係權限請求碼
if(requestCode == GPS.REQUEST_CODE_GPS) {// 羅盤權限請求碼 |
判斷『權限』係咪獲得
if(grantResults[0] == PackageManager.PERMISSION_GRANTED &&
grantResults[1] == PackageManager.PERMISSION_GRANTED) { // 獲得授權 |
再次定位
GPS.UpdatesLocation(); |
交翻畀onRequestPermissionsResult()處理
super.onRequestPermissionsResult(requestCode,permissions,grantResults);
} |
3D游戲基于『透視投影』產生立體效果, 而2D游戲戲用『正交投影』產生平面效果, 生成等比例游戲.
glViewport(0,0,width, height); |
glMatrixMode(GL_PROJECTION); |
glLoadIdentity(); |
gluOrtho( fovy, aspect, zNear, zFar ); |
glMatrixMode(GL_MODELVIEW); |
glLoadIdentity(); |
基於視角『正交投影』
void gluOrtho(double fovy,double aspect,double zNear,double zFar){
double xmin, xmax, ymin, ymax; ymax = zNear * tan(fovy * 3.141592654f / 360.0f); ymin = -ymax; xmin = ymin * aspect; xmax = ymax * aspect; glOrthof(xmin, xmax, ymin, ymax, zNear, zFar); } |
Android Studio設置全屏,通過編輯『themes.xml』
『themes.xml』 |
<style name=”Theme.Fullscreen” parent=”android:Theme.NoTitleBar.Fullscreen”> |
編輯『AndroidManifest.xml』
『AndroidManifest.xml』 |
<application android:theme=”@style/Theme.Fullscreen”> |
代碼係『eclipse』移稙過蒞, 『Android Studio』程式閃退. 因『MainActivity』繼承『AppCompatActivity』造成. 改為繼承『Activity』.
public class MainActivity extends Activity |
OpenGL EGL作為平臺冇関 API, 今程序冇視『Linux X Window』『Microsoft Windows』『Mac OS X Quatz』差異.用統壹接口同原生視窗聯接. 跨平臺API更易於移值. 所以OpenGL比DirectX更得人鐘意.
#include <GLES/gl.h> | 標準OpenGL頭文檔 |
#include <GLES/glext.h> | OpenGL實用架餐庫 |
#include <EGL/egl.h> | EGL庫 |
#include <EGL/eglext.h> | EGL架餐庫 |
#include <android/native_window_jni.h> | 原生視窗庫ANativeWindow |
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
EGL_NO_DISPLAY | 0失敗 |
EGLint major,minor;
eglInitialize(display,&major,&minor) ; |
EGL_BAD_DISPLAY | 非有效EGLDisplay |
EGL_NOT_INITALIZED | 未能初始 |
EGLBoolean eglChooseConfig (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config); |
參數 | 簡介 |
EGLDisplay dpy | 『原生視窗』索引 |
EGLint *attrib_list | 指定屬性列表, 以EGL_NONE結屘 |
EGLConfig *configs | 返回配置列表 |
EGLint config_size | 指定配置列表長度 |
EGLint *num_config | 返回配置長度 |
屬性 | 描述 | 值 |
EGL_SURFACE_TYPE | EGL渲染表面類型 | EGL_WINDOW_BIT |
EGL_RENDERABLE_TYPE | OpenGL版本 | EGL_OPENGL_ES_BIT |
EGL_RED_SIZE | 紅色量位 | 8Bit |
EGL_GREEN_SIZE | 綠色量位 | 8Bit |
EGL_BLUE_SIZE | 藍色量位 | 8Bit |
EGL_ALPHA_SIZE | 透明量位 | 8Bit |
EGL_DEPTH_SIZE | 深度量位 | 16Bit |
EGL_NONE | 屬性列表以EGL_NONE結屘 | 0 |
屬性列表
EGLint attrib_array[32]; |
attrib_array[0] = EGL_SURFACE_TYPE; | attrib_array[1] = EGL_WINDOW_BIT; |
attrib_array[2] = EGL_RENDERABLE_TYPE; | attrib_array[3] = EGL_OPENGL_ES_BIT; |
attrib_array[4] = EGL_RED_SIZE; | attrib_array[5] = 8; |
attrib_array[6] = EGL_GREEN_SIZE; | attrib_array[7] = 8, |
attrib_array[8] = EGL_BLUE_SIZE; | attrib_array[9] = 8; |
attrib_array[10] = EGL_ALPHA_SIZE; | attrib_array[11] = 8; |
attrib_array[12] = EGL_DEPTH_SIZE; | attrib_array[13] = 16; |
attrib_array[14] = EGL_NONE; |
EGLint config_number = 32;
EGLConfig config_array[32]; EGLConfig config;// 配置 eglChooseConfig(display,attrib_array,config_array,config_number,&config_number); |
for(int i=0;i<config_number;++i) {
eglGetConfigAttrib(openGL->display, config_array[i],EGL_RED_SIZE, &red); eglGetConfigAttrib(openGL->display, config_array[i],EGL_GREEN_SIZE, &green); eglGetConfigAttrib(openGL->display, config_array[i],EGL_BLUE_SIZE, &blue); eglGetConfigAttrib(openGL->display, config_array[i],EGL_ALPHA_SIZE, &alpha); eglGetConfigAttrib(openGL->display, config_array[i],EGL_DEPTH_SIZE, &depth); if( red == 8 && green == 8 && blue == 8 && alpha == 8 && depth == 16){ openGL->config = config_array[i];// 最佳配置 break; } } |
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list); |
參數 | 簡介 |
EGLDisplay dpy | 『原生視窗』索引 |
EGLConfig config | 最佳配置 |
EGLNativeWindowType win | SurfaceView |
const EGLint *attrib_list | 屬性列表 |
視窗屬性 | 描述 | 值 |
EGL_RENDER_BUFFER | 渲染緩存 | EGL_SINGLE_BUFFER『單緩存』EGL_BACK_BUFFER『雙緩存』 |
EGL_NONE | 屬性列表以EGL_NONE結屘 | 0 |
attrib_array[0] = EGL_RENDER_BUFFER; | attrib_array[1] = EGL_BACK_BUFFER; |
attrib_array[2] = EGL_NONE; |
surface_draw = eglCreateWindowSurface(display,config,nativeWindow,attrib_array); |
錯誤碼EGLint error = eglGetError(); | 簡介 |
EGL_BAD_MATCH | 視窗唔匹配EGLConfig唔支持渲染 |
EGL_BAD_CONFIG | 視窗唔匹配EGLConfig系統晤支持 |
EGL_BAD_NATIVE_WINDOW | 原生視窗句柄冇效 |
EGL_BAD_ALLOC | 冇法分配資源 |
eglMakeCurrent(display,surface_draw,surface_draw,context); |
ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface) |
SurfaceView surfaceView = (SurfaceView)findViewById(R.id.surface_view); |
SurfaceHolder surfaceHolder = surfaceView.getHolder(); |
surfaceHolder.addCallback(this); |
Surface surface = surfaceHolder.getSurface(); |
『Android Studio NDK』提供『EGL』連接『OpenGL』, 『EGL』被設計出來,作爲 OpenGL 和原生窗口系統之間的橋梁『Microsoft Windows』『Mac OS X Quatz』差異.用統壹接口同原生視窗聯接. 跨平臺API更易於移值. 所以OpenGL比DirectX更得人鐘意.
static {
System.loadLibrary(“app”); } |
<android.view.SurfaceView
android:layout_width=”match_parent” android:layout_height=”match_parent” android:id=”@+id/surface_view” /> |
<style name=”FullscreenTheme” parent=”android:Theme.NoTitleBar.Fullscreen” > |
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) { new Thread(this).start();//渲染,啟動線程 Runnable.run() } |
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { } |
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) { } |
@Override
public void run() { init( ); while (true) { update(0); } } |
<uses-sdk android:minSdkVersion=”9″ android:targetSdkVersion=”19″ /> |
find_library( OpenGL-lib libGLESv1_CM.so ) |
find_library( OpenEGL-lib libEGL.so ) |
find_library( Android-lib libandroid.so ) |
target_link_libraries(app
${OpenGL-lib} ${OpenEGL-lib} ${Android-lib}) |
#include <GLES/gl.h> | 標準OpenGL頭文檔 |
#include <GLES/glext.h> | OpenGL架餐庫 |
#include <EGL/egl.h> | EGL頭文檔 |
#include <EGL/eglext.h> | EGL架餐庫 |
#include <android/native_window_jni.h> | 原生視窗庫 |
將『OpenGL』移稙過『Andord』. 但OpenGL ES偏偏冇gluPerspective()視錐投影.但有glFrustumf()設定視口.
通過glFrustumf()設置視口, 得到 gluPerspective()視錐投影. |
void gluPerspective(double fovy,double aspect,double zNear,double zFar){
double ymax = zNear * tan(fovy * 3.141592654f / 360.0f); double ymin = -ymax; double xmin = ymin * aspect; double xmax = ymax * aspect; glFrustumf(xmin, xmax, ymin, ymax, zNear, zFar); } |
3D相機投影代碼
glViewport(0,0,viewport_width,viewport_height); | 重置視區尺寸 |
glMatrixMode(GL_PROJECTION); | 設定投影矩陣 |
glLoadIdentity(); | 載入單位矩陣 |
gluPerspective(fov,aspect_ratio,near_clip_z,far_clip_z); | 設置視錐體投影 |
glMatrixMode(GL_MODELVIEW); | 設定模型視圖矩陣 |
glLoadIdentity(); | 載入單位矩陣 |
將『風水羅盤』由『Windows』移稙過『Andord』. 係交換緩存畫面『eglSwapBuffers()』返回『EGL_FALSE』, 『eglGetError()』返回『EGL_BAD_SURFACE』.
EGLBoolean ret = eglSwapBuffers(display,surface);
if(ret == EGL_FALSE){ GLint error = eglGetError(); if(error == EGL_BAD_SURFACE){ } } |
係『Windows』有『Winmain()』入口. 可以控制冚個整游戲運作, 係 『Winmain()』 游戲『運行』『渲染』都係主線程行.
而 『Andord』冇『Winmain()』. 游戲『運行』『渲染』要係新線程『Thread()』行. 而『EGLContext』同『EGLSurface』需係同壹線程『Thread()』生成, 唔係『eglSwapBuffers()』投『EGL_BAD_SURFACE』錯誤碼.
知錯係邊就易整, 將egl()程式擺係新線程『Thread()』行,而非『 onSurfaceCreated()』.
新線程『Thread()』 |
EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id); |
EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor); |
EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config); |
EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, const EGLint *attrib_list); |
ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface); |
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list); |
EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx); |
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface); |
係Android游戲唔將游戲資源擺係『res』而係『assets』, 事因『res』限制層次結構, 而『assets』允許任意檔案資料夾層次結構.位於『assets』檔案只讀唔寫. NDK需android 9版先支持.
係『CMakeLists.txt』添加『libandroid.so』庫
CMakeLists.txt |
find_library( Android-lib libandroid.so ) |
target_link_libraries( geomanticcompass
${OpenGL-lib} ${OpenEGL-lib} ${Android-lib} ${log-lib}) |
係c檔案加入頭檔
#include <android/asset_manager.h> |
#include <android/asset_manager_jni.h> |
『assets』檔通過『AssetManager』訪問
AssetManager assetManager = this.getAssets(); |
係『NDK』『assets』檔通過『AAssetManager』訪問, 『AAssetManager』接口
AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager); |
將『AssetManager』傳過去獲得『AAssetManager』
extern “C”
JNIEXPORT void JNICALL Java_net_bookcard_geomanticcompass_MainActivity_init(JNIEnv *env, jobject thiz, jobject egl_config, jobject asset_manager) { AAssetManager *assetManager= AAssetManager_fromJava(env, asset_manager); } |
public native void init(AssetManager asset_manager); |
init (getAssets()); |
開啟『assets』檔, 冇需畀絕對路徑, 而係畀『assets』相對路俓
AAsset *asset = AAssetManager_open(nativeasset, “name.bmp”, AASSET_MODE_BUFFER); |
檔长度
int size = AAsset_getLength(asset) |
分配記憶體
PBYTE data = (PBYTE)malloc(size); |
讀『assets』檔
AAsset_read(asset, data, size); |
載入分析
Load(data, size); |
释放记忆体
free(data); |
閂『assets』檔
AAsset_close(asset); |
將游戲資源擺係『assets』下,『assets』『res』同位於『app\src\main』之下.
檔案資料夾 | 位置 |
assets | D:\Android\game\app\src\main\assets |
cpp | D:\Android\game\app\src\main\cpp |
java | D:\Android\game\app\src\main\java |
res | D:\Android\game\app\src\main\res |
係游戲每『渲染』1『畫面』呌1『幀』Frame, 影書每秒24格『菲林』24『幀』, Android每秒至高60『幀』.
Android Studio NDK
#include <time.h> |
clock_t clock(void) |
#include <unistd.h> |
long sysconf(int name) |
量度程式耗時
long clockTicks = sysconf(_SC_CLK_TCK);// 時鐘信號
clock_t startTime = clock();//當前時鈡 // 調用程式 clock_t currentTime = clock();//當前時鈡 float seconds = (float)(currentTime – startTime) / (float)clockTicks; |
需要struct結構用蒞計『幀』Frame, 公式加下:
float _fps = Frames * (double)(currentTime – startTime) / (float)clockTicks ; |
『幀』Frame |
typedef struct FPS_TYP {
clock_t startTime;// 啓動時鐘 long clockTicks;// 時鐘信號 float Frames;// 每秒渲染幀數 int n;// 幀計 }FPS,*FPS_PTR; |
計算每秒渲染幀 |
float Get_FPS(FPS_PTR fps){
++fps->n;// 幀加壹 if (fps->n > 100) { fps->Frames = Get_FPS(fps, fps->n); fps->n = 0; } return fps->Frames; } |
float Get_FPS(FPS_PTR fps, int Frames){
clock_t currentTime = clock();// 進程時鈡 double _fps = (double)Frames * (double)(currentTime – fps->startTime) / (float)fps->clockTicks ; fps->startTime = currentTime;// 重置時間 return (float)_fps; } |
Android Studio NDK編譯又蒞失敗, 『ninja: error: unknown target ‘clean’, did you mean ‘ninja -t clean’? 』大意目標唔乾淨.
Build command failed. |
Error while executing process C:\Users\bookc\AppData\Local\Android\Sdk\cmake\3.10.2.4988404\bin\ninja.exe with arguments {-C D:\Android\GeomanticCompass\app\.cxx\RelWithDebInfo\3t474c6q\arm64-v8a clean}
ninja: Entering directory `D:\Android\GeomanticCompass\app\.cxx\RelWithDebInfo\3t474c6q\arm64-v8a’ |
ninja: error: unknown target ‘clean’, did you mean ‘ninja -t clean’? |
gluLookAt() UVN相機模型,設定視點(相機)位置和視綫方向(矩陣運算).
void gluLookAt( GLdouble eyeX,
GLdouble eyeY, GLdouble eyeZ, GLdouble centerX, GLdouble centerY, GLdouble centerZ, GLdouble upX, GLdouble upY, GLdouble upZ); |
OpenGL ES冇gluLookAt() , 手工生成『旋轉矩陣』『相機位置』函式
void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez,
GLfloat centerx, GLfloat centery, GLfloat centerz, GLfloat upx, GLfloat upy, GLfloat upz) { GLfloat m[16];// 旋轉矩陣 GLfloat x[3], y[3], z[3]; GLfloat mag; //生成旋轉矩陣 // Z矢量 z[0] = eyex – centerx; z[1] = eyey – centery; z[2] = eyez – centerz; mag = sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]); if (mag) { z[0]/= mag; z[1]/= mag; z[2]/= mag; }
// Y矢量 y[0] = upx; y[1] = upy; y[2] = upz;
// X 矢量 = Y 叉積 Z x[0] = y[1] * z[2] – y[2] * z[1]; x[1] = -y[0] * z[2] + y[2] * z[0]; x[2] = y[0] * z[1] – y[1] * z[0];
// 重新計算 Y = Z 叉積 X y[0] = z[1] * x[2] – z[2] * x[1]; y[1] = -z[0] * x[2] + z[2] * x[0]; y[2] = z[0] * x[1] – z[1] * x[0];
// 叉積給出了平行四邊形的面積,對于非垂直單位長度向量;所以在這裏標準化x,y mag = sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]); if (mag) { x[0]/= mag; x[1]/= mag; x[2]/= mag; }
mag = sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]); if (mag) { y[0]/= mag; y[1]/= mag; y[2]/= mag; }
#define M(row,col) m[col*4+row] M(0, 0) = x[0]; M(0, 1) = x[1]; M(0, 2) = x[2]; M(0, 3) = 0.0; M(1, 0) = y[0]; M(1, 1) = y[1]; M(1, 2) = y[2]; M(1, 3) = 0.0; M(2, 0) = z[0]; M(2, 1) = z[1]; M(2, 2) = z[2]; M(2, 3) = 0.0; M(3, 0) = 0.0; M(3, 1) = 0.0; M(3, 2) = 0.0; M(3, 3) = 1.0; #undef M glMultMatrixf(m);
// 視點(相機)位置 glTranslatef(-eyex, -eyey, -eyez); } |
係NDK調用fprintf ()格式字符函式報錯
fprintf(fp_error,buffer); |
error: format string is not a string literal (potentially insecure) [-Werror,-Wformat-security] |
原解fprintf()缺失參數.加入參數0問題消失.
fprintf(fp_error,buffer,0); |
int printf(const char *format [,argument]…); | 熒屏 |
int fprintf(FILE *stream,const char *format [,argument ]…); | 文檔 |
int sprintf(char *buffer,const char *format [,argument] …); | 記憶體 |
format格式 | argument參數 | 簡介 |
c | 字符 | 單個字符 |
C | 字符 | 單個字符 |
d | 整數 | 帶正負符號十進整數 |
i | 整數 | 帶正負符號十進整數 |
o | 整數 | 冇正負符號八進整數 |
u | 整數 | 冇正負符號十進整數 |
x | 整數 | 冇正負符號,細寫十六進整數.abcdef |
X | 整數 | 冇正負符號,大寫十六進整數.ABCDEF |
e | 浮點數 | 帶正負符號十進浮點數,指數版. |
E | 浮點數 | 帶正負符號十進浮點數, 指數版. |
f | 浮點數 | 帶正負符號十進浮點數 |
F | 浮點數 | 帶正負符號十進浮點數 |
g | 浮點數 | 係%e同%f中選 |
G | 浮點數 | 係%e同%f中選 |
a | ||
A | ||
n | ||
s | 字符串 | 直至首個NULL |
S | 字符串 | 直至首個NULL |
z | ||
% | 指定格式 |
樣式 | 輸出/寫入 |
sprintf (text, ” %2d\n”, 3); | 03 |
sprintf (text, “%s\n “, “abcdef”); | abcdef |
Android 所用C/C++庫Bionic API. 即輕量級C/C++庫. 分32位同64位, 32位『time_t』有效期至2038年, 64位『__time64_t』有效期至3000年.
呌『tm』時間結構
struct tm { | 結構時間 |
int tm_sec; | 秒鐘[0~59] |
int tm_min; | 分鐘[0~59] |
int tm_hour; | 時鐘[0~23] |
int tm_mday; | 號[1~31] |
int tm_mon; | 月份[0~11] 需加1 |
int tm_year; | 年份需加1900, 儒略歴 |
int tm_wday; | 禮拜[0~1],禮拜日Sunday=0 |
int tm_yday; | 天[0~365],1月1號=0 |
int tm_isdst; | 夏令時為正, 否 |
long int tm_gmtoff; | 當前時相對UTC往東時差,即東爲正西爲負. |
const char* tm_zone;}; | 時區縮寫 |
係『Win32 API』你需time()/_time64()獲得時間, 再蒞localtime()/_localtime64()根據時區轉爲本地時間『tm』
Win32 API版本 |
char time_text[280];
struct tm temp_str;// 時間結构 __time64_t temp_int;// 時間整数 _time64(&temp_int);// 获取時間 _localtime64_s(&temp_str ,&temp_int);// 根据時區轉為本地時間 sprintf(time_text, “%4d-%02d-%02d %02d:%02d:%02d”, 1900+temp_str.tm_year,1+temp_str.tm_mon, temp_str.tm_mday, temp_str.tm_hour,temp_str.tm_min,temp_str.tm_sec); |
2021-09-09 23:43:08 |
Android Studio NDK 『Bionic API』版本,可用localtime_r()根據時區獲得本地時間『tm』
Bionic API版本 |
char time_text[280];// 時間文本
struct tm temp_str;// 時間結構 time_t temp_int;// 32位時間整數 temp_int = time(0); localtime_r(&temp_int,&temp_str);// 根據時區轉爲本地時間 sprintf(time_text, “%04d-%02d-%02d %02d:%02d:%02d”, 1900+temp_str.tm_year,1+temp_str.tm_mon, temp_str.tm_mday, temp_str.tm_hour,temp_str.tm_min,temp_str.tm_sec); |
2021-09-09 23:43:08 |
新版Android Studio用CMake編譯so庫. 構建『Native C++』工程已自動生成『CMakeLists.txt』同『native-lib.cpp』, 但C/C++代碼係『native-lib.cpp』調用其它『.cpp』程式皆報錯『error: undefined reference』, 皆因冇將C/C++代碼編譯入so庫.
D:/Android/GeomanticCompass/app/src/main/cpp/native-lib.cpp:14: error: undefined reference to ‘IsEven(int)’ |
睇『CMakeLists.txt』文檔
係『add_library()』尾加入『math.cpp』生成鏈接文檔
add_library( geomanticcompass
SHARED native-lib.cpp math.cpp) |
查找日志庫
find_library(log-lib log) |
將目標文檔與庫文檔進行鏈接
target_link_libraries(geomanticcompass math ${log-lib}) |
Android Studio崩潰後重啟弹出『missing essential plugin』缺必須插件,重装Android Studio問題依舊. 缺失插件可能插件崩潰被屏蔽. 係C: 盘搜索 『disabled_plugins.txt』将入面內容冚辦爛刪.
missing essential plugin
org.jietbrains.android Please reinstall Android Studio from scratch |
C:\Users\bookc\AppData\Roaming\Google\AndroidStudio2020.3\disabled_plugins.txt |
Android唔似Window有Winmain()/main()作程式入口. 而Android用Application作程式入口. 比Activity更早調用,也唔會因方向改變重複調用onCreate().
將Application.onCreate()當main()用. 將程式『初此』Init()冚辦爛摆何係度.
class MainApplication extends Application |
<application android:name=”.MainApplication” ></application> |
public class MainApplication extends Application {
// 全局变量,应用程序句柄
private static MainApplication singleton;
//返回应用程序实例
public static MainApplication getInstance() {
return singleton;
}
//创建应用程序时调用
@Override
public final void onCreate(){
Context context;
singleton = this;//
super.onCreate();
context = this.getApplicationContext();
// 初此
}
// 記憶體低
@Override
public final void onLowMemory() {
super.onLowMemory();
}
// 释放憶體低
@Override
public final void onTrimMemory(int level) {
super.onTrimMemory(level);
}
// 改变配置时调用
@Override
public final void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
}
Android Studio C/ C++編程開發环境由以下組件构成:
Android Studio安装『NDK』同『CMake』
生成C/C++工程
係『native-lib.cpp』下生成C函式
native-lib.cpp |
extern “C” JNIEXPORT jstring JNICALL
Java_net_bookcard_geomanticcompass_Lib_stringFromJNI( JNIEnv* env, jobject /* this */) { std::string hello = “Hello from C++”; return env->NewStringUTF(hello.c_str()); } |
真機調試Android程式
配置『系統變數』
装Android NDK
Android Studio默認安装Android SDK
Android Studio默認安装Java JDK
验証『Java JDK』安装
Android手機『方向切换』『滑出鍵盘』, Android會認為配置改變, 将销毁并重啟程式. 調用activity:onCreate()會大量占用CPU時間. 係游戲造成500ms以上都唔可接受. 編輯『AndroidManifest.xml』係『<activity>』元素『configChanges』属加入『orientation方向切换』同『keyboardHidden滑出鍵盘』『keyboard插入键盘』. 係MainActivity重载onConfigurationChanged ()自己處理, 達置 『方向切换』『滑出鍵盘』冇需再次调周onCreate().
AndroidManifest.xml |
<activity
android:name=”.MainActivity” android:configChanges=”keyboard|keyboardHidden|orientation”> </activity> |
MainActivity |
@Override
public void onConfigurationChanged(Configuration newConfig) { if(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {// 横向 } else if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {// 纵向 } if(newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO ) {// 显示键盘 } super.onConfigurationChanged(newConfig); } |
Android程式默認装係内部存储,因佢有极快讀寫速度, 係Android2.2 後可强制装係SD卡, 但SD讀寫慢, 可能會影响程式表現. 編輯AndroidManifest.xml係manifest添installLocation属. 預設装係外部存储 preferExternal.
AndroidManifest.xml |
<manifest xmlns:android=”http://schemas.android.com/apk/res/android”
package=”net.bookcard.GeomanticCompass” android:installLocation=”preferExternal”> </manifest> |
装Android Studio可簡化搭建開發环境過程. 且集成『Open JDK』同『Android SDK』
Android Studio安装指南
SDK Manager装Android SDK 組件
初建Android 工程Hello World
激活手機/平板開發模式
真機調試Android程式
近日台Canon影印機MF746Cx條USB過機線斷左.其實現在Canon已支持網絡影印.無需再透過電腦影印.
Canon影印機設定WIFI
系統管理器ID: | 7654321 |
系統管理器PIN: | 7654321 |
裝Mopria Paint影印程式
Mopria Paint支持三種影印方法
影印機 | Canon MF745C/746C |
份數 | 1 |
紙張大小 | ISO A4 |
色彩 | 黑色/彩色 |
方向 | 縱向/橫向 |
雙面 | 無/長邊/短邊 |
HANDHEL_GAME_STATION
Android掌機HANDHELD GAME STATION
多年前曾買過部PSP.後借給一女仔後再無返過我手.無意中在網絡睇到Android游戲掌機HANDHELD GAME STATION.可能太多人購買.2019年12月29號落單,直到2020年1月2號先發貨,順豐隔日送達.通過Android模擬器支持掌機游戲.但居然『無法安裝』PSP游戲.可能是類別錯誤設爲PPSSPP有関.只能等『游戲商店』更新修復.
您無法安裝此游戲,因爲此游戲不支持您的設備 |
通過USB接口充電但無送USB叉機.且邊玩邊叉電較慢.且按鍵好硬.因類雜牌游戲掌機.儅不在生成時其『游戲商店』亦不再營運.强烈建議先將全部游戲下載.
硬件 | 簡述 |
CPU | 1.3GMHz 四核 |
GPU | MAIL-400 |
記憶躰 | 雙通道512MB |
TF插卡 | 128GB-MicroSD |
WIFI | 2.4G在綫下載游戲與更新系統.未支持5G網絡 |
電池 | 鋰電4000mAH |
屏幕 | 3.5英寸IPS高清硬屏 |
藍牙 | 4.0無綫手制 |
HDMI接口 | 支持 |
硬體版本 | GBX2000_MB_VD |
固體版本 | GBX2000V1開發版 |
硬件按鍵 | 簡述 |
搖杆 | 街機 |
方向鍵 | 焦點切換 |
HOME鍵 | 短按退出儅前界面
主界面模式長按進入系統設置. |
SELECT鍵 | 街機投幣 |
START鍵 | 開啓游戲 |
+/-鍵 | 調節喇叭音量 |
START鍵+音量鍵 | 調節屏幕亮度 |
X鍵 | 進入游戲商店 |
A鍵 | OK |
B鍵 | CANCEL |
D鍵 | 退出 |
電源鍵 | 長按關機
短按休眠 |
模擬器 | 簡述 |
FC | 任天堂/紅白機 |
SFC | 超級任天堂 |
N64 | 任天堂64 |
DC | |
WS/WSC | 萬代 |
MA/ME | |
FBA | 街機 |
PS | Play Station游戲無發保存 |
PSP | Play Station Portable『游戲商店』無法下載 |
MD | 世嘉 |
GBA | Game Boy Advance |
GB/GBC | Game Boy /Game Boy Color |
PCE | PC Engine |
Android系統通病是越用越慢.近日對台『雜牌古董』Android重寫系統,過程尚算順利. 使用『晶晨燒錄工具』USB Burning Tool進行線擦並把其過程記錄
『跟隨相機』與『歐拉相機』喂一區別在於屬性設置不同.跟隨相機常將它固定在移動物體上.它需要以下屬性:
3D空間位置position
向上向量.相當於在相機上貼上一個向上箭頭up
視點向量即相機視口朝向目標lookAt
遠裁剪面far
近裁剪面near
『視場』即視口角度fieldOfView
視口縱橫比aspectRatio
再移動相機時你需要分被相機『位置』與『視點』.『跟隨相機』生成代碼:
設定相機視口,寬與高為屏幕分辨率
gl.glViewport(0,0,width,height);
設置相機矩陣,將當前堆棧設為投影矩陣
gl.glMatrixMode(GL10.GL_PROJECTION);
棧頂載入單位矩陣
gl.glLoadIdentity();
設置透視投影矩陣.定義視錐體參數.『視口角度』『視口縱橫比』『遠近裁剪面』
GLU.gluPerspective(gl, fieldOfView, aspectRation, near, far);
將當前堆棧設為模型視圖矩陣
gl.glMatrixMode(GL10.GL_MODELVIEW);
棧頂載入單位矩陣
gl.glLoadIdentity();
生成方位矩陣,好處在於能防止出現弄反位置或角度
GLU.gluLookAt(gl, position.x,position.y,position.z,
lookAt.x, lookAt.y, lookAt.z,
up.x, up.y, up.z);
歐拉相機即第一稱射擊遊戲中時用相機.你需要定義以下屬性
『視場』即視口角度fieldOfView
視口縱橫比aspectRatio
遠裁剪面far
近裁剪面near
3D空間位置position
繞y軸旋轉角yaw
繞x軸旋轉角pitch.角度值-90~+90度之間.這類似於頭部傾斜角.若超過這角度則造成骨折.
設定歐拉相機視口,寬與高為屏幕分辨率
gl.glViewport(0,0,width,height);
設置歐拉相機矩陣.首先將當前堆棧設為投影矩陣
gl.glMatrixMode(GL10.GL_PROJECTION);
棧頂載入單位矩陣
gl.glLoadIdentity();
設置投影矩陣.你需定義要視錐體
GLU.gluPerspective(gl, fieldOfView, aspectRatio, near, far);
設為模型視圖矩陣
gl.glMatrixMode(GL10.GL_MODELVIEW);
棧頂載入單位矩陣
gl.glLoadIdentity();
繞x軸旋轉角度
gl.glRotatef(-pitch,1,0,0);
繞Y軸旋轉角度
gl.glRotatef(-yaw,0,1,0);
移動相機,相機位於原點且鏡頭指向z軸負方向
gl.glTranslatef(-position.x,-position.y,-position.z);
獲取相機方向
相機未旋轉時指向z軸負向
float[] inVec = {0,0,-1,1};
float[] outVec = {0,0,0,0};
設置單位矩陣
float[] matrix = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
Matrix.setIdentityM(matrix,0);
繞x軸俯仰
Matrix.rotateM(matrix, 0, yaw,0,1,0);
繞Y軸旋轉
Matrix.rotateM(matrix, 0, pitch,1,0,0);
將矩陣和向量相乘的單位方向向量
Matrix.multiplyMV(outVec, 0, matrix, 0, inVec, 0);
direcion.set(outVec[0],outVec[1],outVec[2]);
當相機遠離模型時,模型也會變小.其紋理渲染採樣時會顆粒狀失真.解決失真問題關鍵在於讓屏幕中體積較小物體或遠離視點時.使用低分辯率『紋理Texture』圖像.稱值為紋理鏈.首先獲取紋理尺寸.然後創建比小分辨率紋理圖,把分辨率縮小一半.重複這一過程直到分辨率為1.為了在OpenGL ES使用紋理鏈,需要執行以下兩步
紋理位圖載入與綁定.在其基礎加入紋理鏈代碼
GL10 gl = GRAPHICS.gl;
生成空的紋理對象,並獲取id
gl.glGenTextures(1, ids,0);
int id = ids[0];
讀取紋理圖
Bitmap bitmap = BITMAP.Read(file_name);
綁定紋理ID
gl.glBindTexture(GL10.GL_TEXTURE_2D, id);
設置紋理屬性,紋理過濾器,指定縮小倍數過濾器
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR_MIPMAP_NEAREST);
指定放大倍數過濾器
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
獲取紋理寬度與高度
int width = bitmap.getWidth();
int height = bitmap.getHeight();
原始圖層索引為0
int level = 0;
while(true) {
上傳紋理
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0);
圖層索引號加一
++level;
計算縮小一半紋理位圖寬與高
int newWidth = bitmap.getWidth()/2;
int newHeight = bitmap.getHeight()/2;
寬與高等於零時跳出循環
if(newWidth == 0)
break;
創建縮小一半紋理位圖
Bitmap newBitmap = Bitmap.createBitmap(newWidth,newHeight,bitmap.getConfig());
Canvas canvas = new Canvas(newBitmap);
canvas.drawBitmap(bitmap,
new Rect(0,0,bitmap.getWidth(),bitmap.getHeight()),
new Rect(0,0,newWidth,newHeight),
null);
回收位圖資源
bitmap.recycle();
bitmap = newBitmap;
}
取消邦定紋理ID
gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
回收位圖資源
bitmap.recycle();
射燈是OpenGL ES中燈光中最耗GPU資源燈光.但其3D效果十分逼真.睇上圖.射燈有多個參數需指定.『位置』『照射方向』『照射角度』小於180度、『衰減指數』小於零,若設為0則亮度不衰減,實制『衰減值』與遊戲3D空間尺寸有關.這裡設為0.01.以及三個光照顏色『環境色』『漫反射色』『鏡面反射色』
public class LightSpot {
private float[] ambient = {1.0f, 1.0f, 1.0f, 1.0f};// 射燈-環境色
private float[] diffuse = {1.0f, 1.0f, 1.0f, 1.0f};// 射燈-漫反射色
private float[] specular = {0.0f, 0.0f, 0.0f, 1.0f};// 射燈-高光顏色
private float[] position = {0.0f, 3.0f, 0.0f, 1.0f};// 射燈-位置
private float[] direction = {0.0f, -1.0f, 0.0f, 0.0f};// 射燈-方向
private float cutoff = 45;// 射燈-角度範圍,缺省為180.0
private float exponent = 0.01f;// 衰減指數,缺省為0.0f
int id = 0;// 光照ID
// 射燈ID 輸入:GL10.GL_LIGHT0至GL10.GL_LIGHT7
LightSpot(int ID){
this.id = ID;
}
//設置射燈方向,轉換為方向向量.最後w分量設0,代表方向
public void setDirection(float x,float y,float z){
direction[0] = x – position[0];
direction[1] = y – position[1];
direction[2] = z – position[2];
direction[3] = 0;
}
// 設定射燈位置.w分量設為1代表位置向量
public void setPosition(float x,float y,float z){
position[0] = x;
position[1] = y;
position[2] = z;
position[3] = 1;
}
// 設定射燈顏色
public void setColor(float r,float g,float b){
ambient[0] = r;
ambient[1] = g;
ambient[2] = b;
ambient[3] = 1;
}
//使能射燈
public void enable(){
GL10 gl = GRAPHICS.gl;
gl.glEnable(id);//使能
gl.glLightfv(id,GL10.GL_AMBIENT, ambient, 0);// 射燈-環境色
gl.glLightfv(id,GL10.GL_DIFFUSE, diffuse, 0);// 射燈-漫反射色
gl.glLightfv(id,GL10.GL_SPECULAR, specular, 0);// 射燈-高光顏色
gl.glLightfv(id,GL10.GL_POSITION, position, 0);// 位置
gl.glLightfv(id,GL10.GL_SPOT_DIRECTION, direction, 0);// 方向
gl.glLightf(id,GL10.GL_SPOT_CUTOFF,cutoff); // 角度範圍
gl.glLightf(id,GL10.GL_SPOT_EXPONENT,exponent);// 衰減指數
}
// 屏蔽射燈
public void disable(){
GL10 gl = GRAPHICS.gl;
gl.glDisable(id);
}
}
物體都由特定材質構成.材質決定照射在物體上光返射方式並會改變反射光顏色.材質為多邊形設置材質屬性用於光照計算,它是全域性影響所有繪製多邊形,直到它在次調用.OpenGL ES中需要為每種材質指定『環境色』『漫反射色』『鏡面反射色』RGBA顏色值.此材質吸收光.只有『光源顏色』與『材質顏色』最小值運算(RGB值)得到『反射光顏色』
材質顏色 | 光源顏色 | 反射光顏色『最小值運算』 |
(0,1,0) | (1,1,1) | (0,1,0) |
(0,1,0) | (1,0,0) | (0,0,0) |
(0,0,1) | (1,1,1) | (0,0,1) |
材質類代碼
public class Material {
private float[] ambient = {1.0f, 1.0f, 1.0f, 1.0f};// 材質-環境色
private float[] diffuse = {1.0f, 1.0f, 1.0f, 1.0f};// 材質-漫反射色
private float[] specular = {1.0f, 1.0f, 1.0f, 1.0f};// 材質-鏡面顏色
//設置材質環境色
public void setAmbient(float r,float g,float b){
ambient[0]=r;
ambient[1]=g;
ambient[2]=b;
ambient[3]=1;
}
// 設置材質漫反射色
public void setDiffuse(float r,float g,float b){
diffuse[0]=r;
diffuse[1]=g;
diffuse[2]=b;
diffuse[3]=1;
}
// 設置材質鏡面反射色
public void setSpecular(float r,float g,float b){
specular[0]=r;
specular[1]=g;
specular[2]=b;
specular[3]=1;
}
//使能材質
public void enable(){
GL10 gl = GRAPHICS.gl;
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_AMBIENT, ambient, 0);// 環境色
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_DIFFUSE, diffuse, 0);// 漫反射色
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_SPECULAR, specular,0);// 鏡面反射色
}
}
定向光具有方向但沒有位置.首先在3D空間定義一個點,而『方向』表示為該點指向『原點』向量之方向.若在3D空間右則射過來定向光
public class LightDirectional {
private float[] ambient = {1.0f, 1.0f, 1.0f, 1.0f};// 點光-環境色
private float[] diffuse = {1.0f, 1.0f, 1.0f, 1.0f};// 點光-漫反射色
private float[] specular = {0.0f, 0.0f, 0.0f, 1.0f};// 點光-高光顏色
private float[] direction = {0, 0, -1, 0};// 定向光-方向
int id = 0;// 燈光ID
構造定向光.燈光id:GL10.GL_LIGHT0至GL10.GL_LIGHT7
public LightDirectional(int ID){
this.id = ID;// 燈光ID
}
設置方向,將位置轉換為方向.將w分量設0,代表方向
public void setDirection(float x,float y,float z){
direction[0] = -x;
direction[1] = -y;
direction[2] = -z;
direction[3] = 0;
}
設置定向光顏色
public void setColor(float r,float g,float b){
ambient[0] = r;
ambient[1] = g;
ambient[2] = b;
ambient[3] = 1;
}
使能設置頂向光顏色環境色、漫反射色、高光顏色、位置.最後一個參數是數組索引
public void enable(){
GL10 gl = GRAPHICS.gl;
gl.glEnable(id);//使能
gl.glLightfv(id,GL10.GL_AMBIENT, ambient, 0);
gl.glLightfv(id,GL10.GL_DIFFUSE, diffuse, 0);
gl.glLightfv(id,GL10.GL_SPECULAR, specular, 0);
gl.glLightfv(id,GL10.GL_POSITION, direction, 0);
}
定向光屏蔽
public void disable(GL10 gl){
gl.glDisable(id);
}
}
點光(燈光)特點是有固定3D位置.首先你需要啟用0號燈光
gl.glEnable(GL10.GL_LIGHT0);
設定燈光顏色,燈光索引為0,最後一個參數是顏色數組偏移量.
float[] ambient = {1.0f, 1.0f, 1.0f, 1.0f};// 點光-環境色
float[] diffuse = {1.0f, 1.0f, 1.0f, 1.0f};// 點光-漫反射色
float[] specular = {0.0f, 0.0f, 0.0f, 1.0f};// 點光-高亮顏色
gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_AMBIENT, ambient, 0);
gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_DIFFUSE, diffuse, 0);
gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_SPECULAR, specular, 0);
設定燈光位置,設定3D空間xyz座標,第四個元素必須設置為1,即光源有位置.
float[] position = {0, 0, 0, 1};// 點光-位置
gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_POSITION, position, 0);
完成渲染後關閉燈光
gl.glDisable(GL10.GL_LIGHT0);
燈光類代碼
public class LightPoint {
private float[] ambient = {1.0f, 1.0f, 1.0f, 1.0f};// 點光-環境色
private float[] diffuse = {1.0f, 1.0f, 1.0f, 1.0f};// 點光-漫反射色
private float[] specular = {0.0f, 0.0f, 0.0f, 1.0f};// 點光-高亮顏色
private float[] position = {0, 0, 0, 1};// 燈光位置
int id = 0;// 燈光ID
構造點光(燈泡) 燈光ID輸入:GL10.GL_LIGHT0至GL10.GL_LIGHT7
public LightPoint(int ID ){
this.id = ID;// 燈光ID
}
設定點光(燈泡)位置
public void setPosition(float x,float y,float z){
position[0] = x;
position[1] = y;
position[2] = z;
position[3] = 1;
}
設定點光(燈泡)顏色
public void setColor(float r,float g,float b){
ambient[0] = r;
ambient[1] = g;
ambient[2] = b;
ambient[3] = 1;
}
使能點光(燈泡)
public void enable(){
GL10 gl = GRAPHICS.gl;
gl.glEnable(id);//使能
gl.glLightfv(id,GL10.GL_AMBIENT, ambient, 0);
gl.glLightfv(id,GL10.GL_DIFFUSE, diffuse, 0);
gl.glLightfv(id,GL10.GL_SPECULAR, specular, 0);
gl.glLightfv(id,GL10.GL_POSITION, position, 0);
}
屏蔽點光(燈泡)
public void disable(){
GL10 gl = GRAPHICS.gl;
gl.glDisable(id);
}
}
環境光是一種特殊光.它沒有位置和方向.它只會均勻照射3D空間中所有物體.在OpenGL ES中啟用全域環境光.
啟用光照
gl.glEnable(GL10.GL_LIGHTING);
環境光純白色,色域範圍為0~1浮點數.影射對應0~255整數
float[] color = {1.0f,1.0f,1.0f,1f};// 環境光浮點數組
設定環境光最後參數color偏移量通常設為0
gl.glLightModelfv(GL10.GL_LIGHT_MODEL_AMBIENT, color, 0);
全域環境光代碼
public class LightAmbient {
static private float[] color = {1.0f,1.0f,1.0f,1f};// 環境光
//設定環境光
static public void setColor(float r,float g,float b ){
color[0] = r;
color[1] = g;
color[2] = b;
color[3] = 1;
}
//使能環境光
static public void enable(){
GL10 gl = GRAPHICS.gl;
gl.glLightModelfv(GL10.GL_LIGHT_MODEL_AMBIENT, color, 0);
}
}
光照系統它可以令3D遊戲更加逼真.要模擬光照需要光源發射光線.和被光照照射物.最後需要一台相機捕足光源發射光以及被物體反射光.光照會改變觀察者對被觀察者物體顏色感受.取卻於以下幾個因素
被照射物體反射光強度取決於光照射到至物體平面時光與物體平面夾角.光與其所照射平面越接近垂直,物體表面反射光強度越大
一旦光照射到平面它會以兩種方式反射.鏡面反射會物體上表現出強光效果.物體鏡面反射效果取決於材質.
漫反射:大部分反射光線會因為物體不規則表面而隨機地發散開來,具有光滑表面
鏡面反射:光照射到光滑鏡面後返射回來,具有粗糙不平整表面是不可能形成
而光照射到表面時反射光顏色還取決於光源和材質顏色.
OpenGL ES可以創建出四種不同光源
環境光源:環境光本身非光源,而是由所在環境中其它光源發出光反射在周圍得到.這些環境光混合形成照明效果.環境光無方向並且被環境光照射所有物體都有共同亮度.
點光源:點光源在空間中有固定位置,並且向個方向照射.如燈泡
定向光源:定向光需要一個方向並延伸至無限遠.如太陽是標準定向光源
投射光源:在3D空間中有固定位置.並且有個照射方向.並且具有錐形照射區域.如街燈就是標準投射光.但很耗GPU資源.
OpenGL ES允許指定光顏色與強度,使用RGBA指定顏色
環境光色:被照射無體整體受到光色.物體將會接受這種顏色照射.並且與光源位置和方向無關.
漫反射色:物體反射時受到光色.背著光源另一面不會被照射到
鏡面反射色:鏡面反射色僅僅影響面向觀察者和光源面
投射色:僅影響錐型照射物
啟用光照,一旦開啟光照系統將被應用於所有渲染.你還需要指定光源與材質以及頂點法線確定最後光照效果
GL10.glEnable(GL10.GL_LIGHTING);
渲染完成必需關閉光照.否則影響之後GUI渲染.
GL10.glDisable(GL10.GL_LIGHTING);
OpenGL ES允許每個場景中最多使用8個光源,外加一個全域光源.光源ID從GL10.GL_LIGHT0至GL10.GL_LIGHT7
光源0使能並將其應用所有渲染物
GL10.glEnable(GL10.GL_LIGHT0);
若想單獨禁用某光源
GL10.glDisable(GL10.GL_LIGHT0);
光源實現代碼 |
環境光 |
定向光 |
燈光 |
射燈 |
材質 |
OpenGL ES矩陣提供以下運算能力
OpenGL ES提供有三種『矩陣』
投影矩陣:建立視錐體形狀和尺寸.它決定投影類型和睇到範圍
模型視圖矩陣:在模型空間中用該矩陣變換3D模型.並將其在3D空間中移動
紋理矩陣:用於動態操縱紋理矩陣
設置當前矩陣為『投影矩陣』
GL10.glMatrixMode(GL10.GL_PROJECTION);
在棧頂載入單位據陣
GL10.glLoadIdentity();
然後剩以正交投影矩陣
GL10.glOrthof(-1,1,-1,1, 1, -1);
設置當前矩陣『模型視圖矩陣』
GL10.glMatrixMode(GL10.GL_PROJECTION);
在棧頂載入單位據陣
GL10.glLoadIdentity();
3D空間中平移
GL10.glTranslatef(x,y,z);
繞Y軸旋轉
GL10.glRoate(angle,0,1,0);
將當前棧頂拷貝並壓入棧頂
GL10.glPushMatrix();
矩陣棧頂出棧
GL10.glPopMatrix();
OpenGL要啟用混合,要將每個頂點顏色ALPHA分量置設為0.5f.這樣模型後方對象都能透過模型睇到
1. OpenGL ES 將在『深度緩存Z-Buffer』和『幀緩存』中渲染模型
2. 當OpenGL ES結合z-buffer啟用混合時.必需確保所有透面對象跟據其距離照相機遠近按升序排序.並且從後往線渲染對象.所有非透明必須在透明對象之前被渲染,而非透明對象不需要經過排序
啟用混合:
1. 啟用深度檢測(Z軸緩存)一定要調用glEnable(GL_DEPTH_TEST);.保證光照正確繪製和模形前後正確繪製
2. 啟用混合gl.glEnable(GL10.GL_BLEND);
3. 設定混合方程式gl.glBlendFunc(GL10.GL_SRC_ALPHA,GL10.GL_ONE_MINUS_SRC_ALPHA);
4. 設定模型透明度gl.glColor4f(1.0f, 1.0f, 1.0f,0.5f);
5. 完成渲染後禁用混合glDisable(GL_BLEND);
當需要在OpenGL ES中啟用混合.需要按以下方式渲染
1. 首先渲染所有不透明對象
2. 將所有透明對象按照其與相機距離運近排序(由遠及近)
3. 渲染排好序透明對象(由遠及近)
混合係數 |
簡介 |
GL_ZERO |
將顏色設為{0,0,0,0} |
GL_ONE |
不改變當前顏色(r,g,b,a)*(1,1,1,1) |
GL_SRC_COLOR |
目標與來源相乘dest (r,g,b,a)* sour (r,g,b,a) |
GL_DST_COLOR |
來源與目標相乘sour (r,g,b,a)* dest (r,g,b,a) |
GL_ONE_MINUS_SRC_COLOR |
(r,g,b,a)*((1,1,1,1)- sour(r,g,b,a)) |
GL_ONE_MINUS_DST_COLOR |
(r,g,b,a)*((1,1,1,1)- dest(r,g,b,a)) |
GL_SRC_ALPHA |
(r,g,b,a) * sour(alpha) |
GL_DST_ALPHA |
(r,g,b,a) * dest(alpha) |
GL_ONE_MINUS_SRC_ALPHA |
(r,g,b,a) * (1- sour(alpha)) |
GL_ONE_MINUS_DST_ALPHA |
(r,g,b,a) * (1- dest(alpha)) |
GL_SRC_ALPHA_SATURATE |
(r,g,b,a) *MIN (sour(alpha),1-dest(alpha)) |
OpenGL中『幀緩存』用於儲存屏幕每個像素.而z-buffer『深度緩存』則儲存像素『深度值』.『深度值』為3D空間中對應點與相機『近裁剪面』距離.
OpenGL ES將z-buffer『深度緩存』為每個像素寫入深度值.OpenGL ES會初此『深度緩存』每個深度值為無窮大(大數).
gl.glEnable(GL10.GL_DEPTH_TEST);
每當渲染像素時將像素深度值和『深度緩存』深度值進行比較.如果深度值更小則表示它接近於『近裁剪面』則更新『幀緩存』像素與『深度緩存』深度值.這一過程稱為『深度測試』.如果未能通過『深度測試』則像素與深度值均不寫入『幀緩存』與『深度測試』
每幀渲染時『幀緩存』與『深度緩存』均需清零.否則上一幀數據會被保留下來
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
在渲染所有3D場景後需關閉『深度測試』.因為渲染2D圖形UI元素無Z軸座標.更無需深度測試.渲染順序由代碼繪製順序決定.
gl.glDisable(GL10.GL_DEPTH_TEST);
2D遊戲使用『正交投影』這意味著模型與視點距離無論多遠,其屏幕尺寸大小總為一至.而3D遊戲則使用『透視投影』模型離視點越近其屏幕尺寸越大.而模型離視點越遠其屏幕尺寸越細.
在『正交投影』就像置身於『矩形盒』.而『透視投影』就像切掉『金字塔』頂部,頂部為『近裁剪面』底部為『遠裁剪面』.而另外四面則分別為『左裁剪面』『右裁剪面』『頂裁剪面』『底裁剪面』
透視錐體由四個參數組成
1.『近裁剪面』與相機矩離
2.『遠裁剪面』與相機矩離
3.視口縱橫比,即視口『近裁剪面』寬高比
4.『視場』指定視錐體寬,也就是它所容納場景
桌面OpenGL帶有GLU輔助函式庫.而Android系統也含有GLU庫.設置投影矩陣
GLU.gluPerspective(GL10 gl,float fieldOfView,float aspectRatio,float near,flat far);
該函式將『透視投影矩陣』與當前矩陣相乘.
gl:為GL10實例
fieldOfView:視場角度,人眼視場角大約67度.加減此值可調整橫向視察範圍
aspectRatio:視口縱橫比,此值為一浮點數
near:遠裁剪面與相機距離
far:近裁剪面與相機距離
『透視投影』代碼
GL10 gl = GRAPHICS.GetGL();
清屏
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
設定視口
gl.glViewport(0,0, GRAPHICS.GetWidth(),GRAPHICS.GetHeight());
設定當前矩陣為投影矩陣
gl.glMatrixMode(GL10.GL_PROJECTION);
載入單位矩陣
gl.glLoadIdentity();
設置投視投影
GLU.gluPerspective(gl, fieldOfView, aspectRatio, near, far);
設定當前矩陣為模型視圖矩陣
gl.glMatrixMode(GL10.GL_MODELVIEW);
載入單位矩陣
gl.glLoadIdentity();
在進入豐富多彩3D世界中需要定以『視錐體』和『精靈頂點』.3D空間中頂點需有xyz座標.並且使用『透視投影』.距離相機越遠,物體越小.離相機較近對象覆蓋較進隊象.3D頂點包含『位置(xyz)』『顏色(rgba)』『法線(x,y,z)』並且完成3D頂點『模型』渲染
public class VERTICES3D {
擁有頂點顏色
boolean hasColor = false;
擁有紋理坐標
boolean hasTexCoord =false;
擁有法線
boolean hasNormal = false;
每個頂點所占空間
int vertex_size = 0;
最大頂點量
int vertex_max = 0;
最大索引量
int index_max = 0;
頂點數組
IntBuffer vertex_array = null;
索引數組
ShortBuffer index_array = null;
頂點緩存
int[] vertex_Buffer;
構造函式分配頂點記憶體,vertex_max為最大頂點量,index_max為最大索引量
VERTICES3D(int vertex_max,int index_max,boolean hasColor,boolean hasTexCoord,boolean hasNormal){
ByteBuffer buffer = null;
this.vertex_max = vertex_max;
this.index_max = index_max;
this.hasColor = hasColor;// 是否擁有頂點顏色
this.hasTexCoord = hasTexCoord;// 是否擁有紋理坐標
this.hasNormal = hasNormal;// 是否擁有法線
計算每頂點所占大小.顏色(rgbs)占4單元.紋理座標(uv)占2單元.3D座標(xyz)占3單元.每個整數占4字節
this.vertex_size = (3 + (hasColor ? 4 : 0) + (hasTexCoord ? 2 : 0) + (hasNormal ? 3: 0)) * 4;
vertex_Buffer = new int[vertex_max * vertex_size / 4];
因為OpenGL ES是以C API結口提供.無法直接使用JAVA數組.因此你需要C數組系統堆棧記憶體.而非JAVA虛擬機記憶體.需要 FloatBuffer分配頂點記憶體.vertex_max為最大頂點量.
buffer = ByteBuffer.allocateDirect(vertex_size * vertex_max);
將『網絡字節』改為『主機字節』或稱為『CPU字節』
buffer.order(ByteOrder.nativeOrder());
獲取頂點整數數組
vertex_array = buffer.asIntBuffer();
每索引占兩BYTE.即OpenGL ES每次最多渲染65536個頂點.
if(index_max > 0){
每個短整形占兩個字節.index_max為最大索引量
buffer = ByteBuffer.allocateDirect(index_max * Short.SIZE/8);
將『網絡字節』改為『主機字節』
buffer.order(ByteOrder.nativeOrder());
獲取頂點短整數數組
index_array = buffer.asShortBuffer();
}
}
將頂點提交給OpenGL數組並觸發
public void setVertices(float[] vertices,int offset,int count){
清空緩存.設定當前位置
vertex_array.clear();
int len = offset + count;
for(int i=offset, j=0; i < len; i++, j++)
vertex_Buffer[j] = Float.floatToRawIntBits(vertices[i]);
寫入數據.移動當前位置
vertex_array.put(vertex_Buffer, 0, count);
觸發
vertex_array.flip();
}
將頂點索引提交給OpenGL數組
public void setIndices(short[] indices,int offset,int count) {
清空緩存.設定當前位置
index_array.clear();
寫入數據.移動當前位置
index_array.put(indices, offset, count);
觸發
index_array.flip();
}
獲取索引量
public int getNumIndices(){
return index_array.limit();
}
獲取頂點量
public int getNumVertices(){
return vertex_array.limit() / (vertex_size/4);
}
綁定頂點數據
public void Bind(){
GL10 gl = GRAPHICS.gl;
啟用頂點數組
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
設置當前寫入位置0
vertex_array.position(0);
設置頂點指針,每個頂點包含xyz分量
gl.glVertexPointer(3, GL10.GL_FLOAT, vertex_size, vertex_array);
擁有頂點顏色
if(hasColor == true) {
啟用頂點顏色數組
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
設置當前寫入位置3
vertex_array.position(3);
設置顏色指針,RGBA四分量
gl.glColorPointer(4, GL10.GL_FLOAT, vertex_size, vertex_array);
}
擁有紋理坐標
if(hasTexCoord == true){
啟用紋理坐標
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
設置當前寫入位置
vertex_array.position(hasColor?7:3);
設置紋理坐標指針UV分量
gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertex_size, vertex_array);
}
擁有法線
if (hasNormal = true) {
啟用法線
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
int offset = 3;
if (hasColor)
offset += 4;
if (hasTexCoord)
offset += 2;
設置當前寫入位置
vertex_array.position(offset);
設置法線指針,xyz分量
gl.glNormalPointer(GL10.GL_FLOAT, vertex_size, vertex_array);
}
}
取消綁定數據
public void Unbind(){
GL10 gl = GRAPHICS.gl;
關閉頂點紋理數組
if(hasColor)
gl.glDisableClientState(GL10.GL_COLOR_ARRAY );
關閉頂點顏色數組
if(hasTexCoord)
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
關閉法線數組
if (hasNormal)
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
}
繪畫3D模型.需要先綁3D頂點
public void Draw(int mode,int offset,int count){
GL10 gl = GRAPHICS.gl;
繪畫頂點
if(index_array != null) {// 繪畫
index_array.position(offset);
gl.glDrawElements(mode, count, GL10.GL_UNSIGNED_SHORT, index_array);
}
else {
gl.glDrawArrays(mode, offset, count);
}
}
}
與『2D矢量』相比『3D矢量』僅是在x,y軸座標加上z軸座標.還有『點積』和『叉積』運算.與繞軸旋轉算法. 無左計算向量角度函式.
public class VECTOR3D {
3D浮點數座標
public float x,y,z;
角度轉弧度
public static float DEGREES_TO_RADIANS = ((1.0f/180.0f)* (float)Math.PI);
弧度轉角度
public static float RADIANS_TO_DEGREES = ((1.0f/(float)Math.PI)*180.0f);
用於繞軸旋轉
private static final float[] matrix = new float[16];
private static final float[] inVec = new float[16];
private static final float[] outVec = new float[16];
購造函式並設定x,y,z
public VECTOR3D(float x, float y,float z){
this.x = x;
this.y = y;
this.z = z;
}
拷貝3D矢量
public VECTOR3D Copy(){
VECTOR3D v;
v=new VECTOR3D(x,y,z);
return v;
}
重設3D矢量數值
public VECTOR3D set(VECTOR3D v){
this.x = v.x;
this.y = v.y;
this.z = v.z;
return this;
}
3D矢量加法運算
public VECTOR3D add(VECTOR3D v){
this.x = this.x + v.x;
this.y = this.y + v.y;
this.z = this.z + v.z;
return this;
}
3D矢量減法運算
public VECTOR3D sub(VECTOR3D v){
this.x = this.x – v.x;
this.y = this.y – v.y;
this.z = this.z – v.z;
return this;
}
3D矢量乘法(即縮放)
public VECTOR3D mul(float scalar){
this.x = this.x * scalar;
this.y = this.y * scalar;
this.z = this.z * scalar;
return this;
}
計算3D矢量長度
public float Len(){
float len;
len = (float) Math.sqrt(x*x+y*y+z*z);
return len;
}
3D矢量單位化,長度為1
public VECTOR3D normer(){
float len;
len = Len();
if(len != 0){
x = x / len;
y = y / len;
z = z / len;
}
return this;
}
繞某軸旋轉,先定義3D矢量,然後設置矩陣為零,然後用rotateM()旋轉,在乘以3D向量
public VECTOR3D rotate(float angle,float axisX,float axisY,float axisZ){
inVec[0] = x;
inVec[1] = y;
inVec[2] = z;
inVec[4] = 1;
Matrix.setIdentityM(matrix, 0);
Matrix.rotateM(outVec,0, angle, axisX, axisY, axisZ);// 選轉
Matrix.multiplyMV(outVec, 0, matrix, 0, inVec, 0);
x = outVec[0];
y = outVec[1];
z = outVec[2];
return this;
}
計算兩3D矢量之間距離
public float Dist(VECTOR3D v){
float distX = this.x – v.x;
float distY = this.y – v.y;
float distZ = this.z – v.z;
float dist = (float)Math.sqrt(distX*distX + distY*distY + distZ*distZ);
return dist;
}
計算兩個3D矢量之間距離平方
public float DistSquared(VECTOR3D v){
float distX = this.x – v.x;
float distY = this.y – v.y;
float distZ = this.z – v.z;
float dist = distX*distX + distY*distY + distZ*distZ;
return dist;
}
計算兩個3D向量叉積,叉積是一個向量,它與va和vb垂直.
void cross(VECTOR3D va, VECTOR3D vb){
x = ( (va.y * vb.z) – (va.z * vb.y) );
y = -( (va.x * vb.z) – (va.z * vb.x) );
z = ( (va.x * vb.y) – (va.y * vb.x) );
}
計算3D向量點積.返回值為浮點數
float dot(VECTOR3D v){
return( (x * v.x) + (y * v.y) + (z * v.z) );
}
3D向量取反數
VECTOR3D inverse(){
this.x = -this.x ;
this.y = -this.y ;
this.z = -this.z ;
return this;
}
}
遊戲動畫由關鍵幀(keyframe)組成.將它地依次連續渲染產生運動效果.上圖每幀尺寸64*64像素一共四幀.為產生動畫效果需每隔若干毫秒渲染一幀.當渲染到最後一幀時可選擇重新播放或停止播放.通常要實現『行走』『跳躍』『攻擊』『倒下』需要定義動畫結構
public class ANIMATION {
保存紋理圖檔只能關鍵幀位置.並且其順序與動畫回放順序相同
public REGION[] frames = null;
保存每幀間隔時間,用於確定每幀切換時間.
public float duration;
動畫播放模式,分為『循環播放』與『單次播放』
public static final int LOOPING = 0;
public static final int NONLOOPING = 1;
構建動畫輸入『每幀間隔』與每幀『紋理區域』
ANIMATION(float duration,REGION … frames){
this.duration = duration;
this.frames = frames;
}
輸入時間提取關鍵幀.播放模式是單次或循環,基於『狀態時間』除以『每幀間隔』計算幀索引.
public REGION GetKeyFrame(float stateTime,int mode){
int index = (int)(stateTime/duration);
if(mode == NONLOOPING)// 單次播放
index = Math.min(frames.length-1, index);
else
index = index % frames.length;
return frames[index];
}
在遊戲中渲染字符,數字.在這裡介紹『點陣字體』技術.每個子圖表示單個字符如0~9.如上圖.『點陣字體』遊戲中渲染文本已非常古老.它通常包含ASCII碼字符圖像.此圖像程為『圖像字符』(glyph).ASCII字符集有128個字符.但只有95個可打印字符.索引號從32到126個.為節約空間『點陣字體』只包含可打印字符.95個字符每行16個字符分6行.ASCII只適用於存儲和顯示標準『拉丁字母』.但已滿足大部分單機遊戲.首先創建96個紋理區域.每區域映射到『點陣字體』中某圖像字符.
public REGION[] array = new REGION[96];
因為ASCII頭32個字符非打印字符無在『圖像字符』中渲染.首字符是空格只要講String轉為char字符減去空格
c = text.charAt(i) – ‘ ‘;
即可獲得紋理區域數組索引.要注意索引值必需在0~95之間否則會越界訪問.
region = array[c];
渲染ASCII文本.這裡簡單起見字體只時用『固定寬度』.但現代文本渲染字體寬度是非固定.可為每個字符設定不同字寬.
BATCHER.Draw(x, y, width, height, region);
上圖我使用Photoshop生成『圖像字符』.但其實有專門免費工具Codehead’s Bitmap Font Generator簡稱CBFG.可從www.codehead.co.uk/cbfg/下載
2D遊戲紋理渲染必須將『背景色』去除.JPEG格式不支持存儲像素點alpha值.將透明色alpha值設為零.需使用PNG格式.若紋理圖像沒有alpha通道時OpenGL ES自動將alpha設為1.但混合開銷很大而目前手機上GPU都不能對大量像素禁行混合.所以在需要時才啟用Blend『混合』.在OpenGL ES中啟動半透明混合處理
gl.glEnable(GL10.GL_BLEND);
設定『來源色』和『目標色』組合方程.
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
啟用2D紋理渲染
gl.glEnable(GL10.GL_TEXTURE_2D);
渲染三角形
…
禁用2D紋理渲染
gl.glDisable(GL10.GL_TEXTURE_2D);
結束渲染後禁用混合
gl.glDisable(GL10. GL_BLEND);
遊戲主選單 『屏幕』通常有 『圖標』『選單 』.所要做是『觸摸』選單 時切換『屏幕』.計算出選單 項『矩形區域』與觸碰點重疊時切換『屏幕』在這裡設定四個選單 項.你需要繪畫『文本紋理』寬高相等並是2倍數.背景色設為透明色.並且保存為『.PNG』. 『文本紋理』好在於研發時用英文文本之後再漢化.
你需定義『2D相機』屏幕原點在左下角、VECTOR2D『點』把觸碰座標轉換屏幕座標、渲染『圖標』區域.定義四個按鈕『新遊戲』『繼續遊戲』『高分榜』『遊戲設定』每當更新屏幕update()對四個選單 區域進行overlap()『矩形碰撞測試』.而渲染『屏幕』時則先渲染『背景』後渲染『選單』
選單項 | 簡介 |
NEW | 啟動新遊戲 |
PLAY | 繼續遊戲按扭 |
Highscores | 高分排名榜 |
Settings | 遊戲設定 |
public class ScreenMain extends SCREEN{
private CAMERA2D camera;
private VECTOR2D touchPoint;
private RECT2D Bounds_Logo;
private RECT2D Bounds_New;
private RECT2D Bounds_Play;
private RECT2D Bounds_Highscores;
private RECT2D Bounds_Settings;
購造入口屏幕
public ScreenMain(){
camera = new CAMERA2D(320, 480);// 相機
Bounds_Logo = new RECT2D(WORLD.PIXEL_WIDTH/2, WORLD.PIXEL_HEIGHT/2 + 160 , 256, 128);
Bounds_New = new RECT2D(WORLD.PIXEL_WIDTH/2, WORLD.PIXEL_HEIGHT/2 + 64, 128, 32);
Bounds_Play = new RECT2D(WORLD.PIXEL_WIDTH/2, WORLD.PIXEL_HEIGHT/2 + 32, 128, 32);
Bounds_Highscores = new RECT2D(WORLD.PIXEL_WIDTH/2, WORLD.PIXEL_HEIGHT/2 , 256, 32);
Bounds_Settings = new RECT2D(WORLD.PIXEL_WIDTH/2, WORLD.PIXEL_HEIGHT/2 – 32, 256, 32);
touchPoint = new VECTOR2D();// 觸碰點
}
更新屏幕都觸碰座標
@Override
public void update(float deltaTime){
for(int i = 0; i < TOUCH.Point_Count; i++){
int action = TOUCH.Point_Action[i];
if(action != TOUCH.ACTION_UP)
continue;
float x = TOUCH.Point_X[i];
float y = TOUCH.Point_Y[i];
touchPoint.set(x, y);
camera.TouchToWorld(touchPoint);//觸摸坐標轉世界坐標
if(Bounds_New.overlap(touchPoint)){// 新遊戲按扭
TOUCH.clear();// 清空
SOUND.Play(ASSETS.sound_click);// 點擊
GAME.setScreen(GAME.getGameScreen());// 遊戲按扭
return;
}
else
if(Bounds_Play.overlap(touchPoint)){// 遊戲按扭
SOUND.Play(ASSETS.sound_click);// 點擊
GAME.setScreen(GAME.getGameScreen());// 遊戲按扭
TOUCH.clear();// 清空
break;
}
else
if(Bounds_Highscores.overlap(touchPoint) ){// 高分按扭
SOUND.Play(ASSETS.sound_click);// 點擊
GAME.setCurrentScreen(GAME.getScoreScreen());
TOUCH.clear();// 清空
break;
}
else
if(Bounds_Settings.overlap(touchPoint)){// 設定按鈕
SOUND.Play(ASSETS.sound_click);// 點擊
GAME.setScreen(GAME.getSettingsScreen());
TOUCH.clear();// 清空
break;
}
}
TOUCH.clear();// 清空
}
渲染屏幕
@Override
public void present(float deltaTime){
GL10 gl = GRAPHICS.gl;
GLSurfaceView view = GRAPHICS.gl_View;
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
camera.SetViewportAndMatrices();
gl.glEnable(GL10.GL_TEXTURE_2D);
BATCHER.Begin(ASSETS.texture_background);
BATCHER.Draw(WORLD.PIXEL_WIDTH/2,WORLD.PIXEL_HEIGHT/2, WORLD.PIXEL_WIDTH, WORLD.PIXEL_HEIGHT, ASSETS.region_background);// 渲染背景
BATCHER.End();
// 設為透明
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
BATCHER.Begin(ASSETS.texture_text);
BATCHER.Draw(Bounds_Logo, ASSETS.region_logo);// 圖標按扭
BATCHER.Draw(Bounds_New, ASSETS.region_new);// 圖標按扭
BATCHER.Draw(Bounds_Play, ASSETS.region_play);// 主選單
BATCHER.Draw(Bounds_Highscores,ASSETS.region_highscores);// 高分按扭
BATCHER.Draw(Bounds_Settings, ASSETS.region_settings);// 設定按扭
BATCHER.End();
gl.glDisable(GL10.GL_TEXTURE_2D);// 禁用
}
暫停保存設置
@Override
public void pause(){
}
恢復
@Override
public void resume(){
}
清除/銷毀
@Override
public void dispose(){
}
返回鍵
@Override
public boolean back(){
return true;
}
}
遊戲設計中通常『更新』『渲染』放在同一線程中.在Windows可以在主線程將『消息驅動』改為『實時驅動』.把『更新』『渲染』放在主線程中.而Android卻無法做到這點.但提供GLSurfaceView可建立獨立線程在背後實現『更新』『渲染』.你需要實現監聽接口GLSurfaceView.Renderer.並註冊到GLSurfaceView中.監聽接口需要分別重寫『創建』『調整大細』『渲染』.GLSurfaceView.Renderer可獲得GL10.通過它方可訪問OpenGL ES API.而GL10中10代表OpenGL ES 1.0標準.可以將GLSurfaceView封裝成獨立控件.從而在layout『佈局』中嵌入.
public class RenderView extends GLSurfaceView implements GLSurfaceView.Renderer {
每當Activity恢復或啟動創建. EGLConfig設置Surface顏色與深度
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig);
當view尺寸發生改變時調用,傳入寬與高
public void onSurfaceChanged(GL10 gl10, int width, int height);
調用『渲染』『更新』完成幀繪製.但每秒不超過60幀.
public void onDrawFrame(GL10 gl10);
令外還需重寫『恢復』『暫停』
『恢復』重啟渲染線程,在Activity恢復顯示時在Activity.onResume()中調用
public void onResume();
『暫停』退出渲染線程,當Activity進入後臺時在Activity.onPause()中調用
public void onPause();
}
編輯layout『佈局』文檔main.xml添加
<net.bookcard.aa.RenderView
android:id=”@+id/render_view”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent” />
定義view狀態
public static final int STATE_INIT = 0;// 初此
public static final int STATE_RUN = 1;// 運行
public static final int STATE_PAUSED = 2;// 暫停
public static final int STATE_FINISHED = 3;// 結束
public static final int STATE_IDLE = 4;// 閒置
int view_width,view_height;// 寬與高
int state = STATE_INIT;// 初此狀態
long startTime ;// 啟動時間
創建Surface獲取屏幕
public void onSurfaceCreated(GL10 gl, EGLConfig eglConfig){
SCREEN screen = GAME.getCurrentScreen();// 當前屏幕
if(state == STATE_INIT) {// 初此
Init(gl, this);
screen = GAME.getMainScreen();
GAME.setCurrentScreen(screen);
}
else {// 重新載入資源
ASSETS.reload();
}
screen = GAME.getCurrentScreen();
state = STATE_RUN;// 運行
startTime = System.nanoTime();// 獲取啟動時間
}
大小發生改變
public void onSurfaceChanged(GL10 gl, int width, int height){
this.view_width = width;// 寬
this.view_height = height;// 高
}
更新並渲染.System.nanoTime()返回納秒, 1000000000納秒=1秒.通過兩次時間測量計算間隔時間
public void onDrawFrame(GL10 gl){
SCREEN screen = GAME.getCurrentScreen();
if(state == STATE_RUN){// 運行
float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f;
startTime = System.nanoTime();// 獲取當前時間
screen.update(deltaTime);// 更新
screen.present(deltaTime);// 渲染
}
else
if(state == STATE_PAUSED) {// 暫停
}
else
if(state == STATE_FINISHED) {// 結束
}
}
恢復渲染在Activity.onResume()中調用
public void onResume(){
super.onResume();
}
暫停渲染在Activity.onPause()中調用
public void onPause(){
state = STATE_PAUSED;// 暫停
super.onPause();
}
初此遊戲系統個部件
void Init(GL10 gl, GLSurfaceView view){
GRAPHICS.Init(gl,view);
SYSTEM.Init(context);// 系統
FileIO.Init(context);// 文件讀寫
BITMAP.Init(context);// 位圖
SOUND.Init(context,100);// 聲音
MUSIC.Init(context,100);// 音樂
TOUCH.Init(this);// 觸摸模塊
GAME.Init();// 屏幕切換系統
BATCHER.Init(1500);// 精靈批處理
ASSETS.load();// 資源
}
你必須登入才能發表留言。