Android平台Doom游戏开发:音频、接口与原生层实现
立即解锁
发布时间: 2025-08-26 00:32:33 阅读量: 3 订阅数: 9 


Android游戏开发:从零开始构建经典射击游戏
# Android平台Doom游戏开发:音频、接口与原生层实现
## 1. 音频处理
### 1.1 音频方法
在Doom游戏的Android开发中,有两个重要的音频方法:
- `stopMusic (String key)`:该方法用于停止由`key`指定的背景音乐。
- `setMusicVolume (int vol)`:此方法用于设置背景音乐的音量,`vol`的取值范围是0到100。
### 1.2 音频类与文件处理
`AudioClip`类与Wolfenstein 3D的`AudioClip`类基本相同,只是包名被重命名了。由于音频文件(包括背景音乐)可能会占用超过5MB的空间,因此这些文件被打包成一个zip存档,并在运行时安装到SD卡中,这样可以节省主文件系统的宝贵磁盘空间。这个zip存档位于Doom项目的`assets`文件夹中。
### 1.3 避免媒体扫描问题
Android具有自动媒体扫描服务,会在文件系统中搜索音频文件。这可能会导致媒体播放器突然显示数百个不需要的Doom音效和音乐。为了解决这个问题,可以在声音文件夹中添加一个名为`.nomedia`的空文件,这样媒体扫描器就会跳过该目录。
## 2. 原生接口类
### 2.1 概述
原生接口类是一个双向管道,它通过原生方法将消息从Java发送到Doom引擎,并使用C到Java的回调将消息从引擎发送回Java。该类由三部分组成:回调监听器、原生方法和C到Java的回调。
### 2.2 回调监听器
回调监听器由`EventListener`接口实现,希望接收C到Java回调的客户端(在这种情况下是主活动`DoomClient.java`)必须实现该接口。C到Java的回调如下:
- `OnMessage(String text, int level)`:主要是一个调试回调,用于发送消息,让Java了解原生端的情况。
- `OnInitGraphics(int w, int h)`:这是第一个回调,仅在图形初始化后触发一次,它会告诉Java视频缓冲区的宽度和高度。
- `OnImageUpdate(int[] pixels)`:每秒会触发多次,将Android打包的视频缓冲区发送到Java,Java将使用它来渲染游戏位图。
- `OnFatalError(String text)`:当引擎中出现不可恢复的错误时触发,接收者应显示消息并终止程序。
- `OnQuit(int code)`:当用户退出游戏时触发,将返回代码发送回接收者。
- `OnStartSound(String name, int vol)`:当原生引擎启动一个声音时触发,委托给接收者处理。
- `OnStartMusic(String name, int loop)`:在背景音乐启动时触发,委托给接收者处理。
- `OnStopMusic(String name)`:在停止背景音乐时触发,委托给接收者处理。
- `OnSetMusicVolume(int volume)`:当用户设置音乐音量时触发,委托给接收者处理。
### 2.3 原生方法
原生方法用于调用原生Doom引擎,有三个基本的原生方法:
- `static native int DoomMain(String[] argv)`:该方法调用Doom引擎的主游戏循环。
- `static native int keyEvent(int type, int key)`:该方法将按键事件发送到引擎,事件类型可以是`EV_KEYDOWN`或`EV_KEYUP`,参数`key`必须是ASCII符号,而不是Android键码,这意味着在发送到引擎之前必须对键码进行转换。
- `static native int motionEvent(int b, int x, int y)`:该方法将运动事件(如用户在显示屏上拖动手指)发送到引擎,第一个参数是鼠标按钮(在这种情况下始终为零),加上事件本身的x和y坐标。
`static native int DoomMain(String[] argv)`需要一个参数列表并会阻塞执行,因此必须在一个线程中运行。以下是一些重要的参数:
- `width`:定义视频缓冲区的宽度。
- `height`:定义视频缓冲区的高度。
- `iwad`:定义要玩的游戏,引擎支持以下游戏文件:
- `doom1.wad`:Doom的共享软件版本。
- `doom.wad`:零售版本。
- `doom2.wad`:Doom系列的第二集。
- `plutonia.wad`:Plutonia Experiment集,是终极Doom系列的一部分。
- `tnt.wad`:名为Evilution的一集,也是终极Doom系列的一部分。
- `file`:定义引擎要使用的额外游戏文件。
例如,要以横向模式玩Doom共享软件,必须作为`String`数组发送给`DoomMain`的参数列表将是`doom -width 480 -height 320 -iwad doom1.wad`。
### 2.4 C到Java回调
C到Java回调用于将引擎消息委托给监听器活动。原生接口类使用一个私有监听器和一个静态设置方法:
```java
private static EventListener listener;
public static void setListener(EventListener l) {
listener = l;
}
```
需要注意的是,只能有一个监听器。当Doom引擎发送消息时,原生接口类会将其委托给监听器来处理事件:
```java
private static void OnMessage(String text, int level) {
if (listener != null)
listener.OnMessage(text, level);
}
```
以下是完整的原生接口类代码:
```java
package doom.jni;
import android.util.Log;
public class Natives {
public static final String TAG = "Natives";
private static EventListener listener;
public static final int EV_KEYDOWN = 0;
public static final int EV_KEYUP = 1;
public static final int EV_MOUSE = 2;
public static interface EventListener {
void OnMessage(String text, int level);
void OnInitGraphics(int w, int h);
void OnImageUpdate(int[] pixels);
void OnFatalError(String text);
void OnQuit(int code);
void OnStartSound(String name, int vol);
void OnStartMusic(String name, int loop);
void OnStopMusic(String name);
void OnSetMusicVolume(int volume);
}
public static void setListener(EventListener l) {
listener = l;
}
/**
* Send a key event to the native layer
*
* @param type : key up down or mouse
* @param sym: ASCII symbol
*/
public static void sendNativeKeyEvent(int type, int sym) {
try {
Natives.keyEvent(type, sym);
} catch (UnsatisfiedLinkError e) {
Log.e(TAG, e.toString());
}
}
// Native Main Doom Loop: @param argv: program arguments
public static native int DoomMain(String[] argv);
/**
* Send a Key Event
* @param type: event type: UP/DOWN
* @param key: ASCII symbol
*/
public static native int keyEvent(int type, int key);
/***********************************************************
* C to Java - Callbacks
***********************************************************/
/**
* This fires on messages from the C layer
*/
private static void OnMessage(String text, int level) {
if (listener != null)
listener.OnMessage(text, level);
}
private static void OnInitGraphics(int w, int h) {
if (listener != null)
listener.OnInitGraphics(w, h);
}
private static void OnImageUpdate(int[] pixels) {
if (listener != null)
listener.OnImageUpdate(pixels);
}
private static void OnFatalError(String message) {
if (listener != null)
listener.OnFatalError(message);
}
private static void OnQuit(int code) {
if (listener != null)
listener.OnQuit(code);
}
/**
* Fires when a sound is played in the C layer.
*/
private static void OnStartSound(byte[] name, int vol) {
if (listener != null)
listener.OnStartSound(new String(name), vol);
}
/**
* Start background music callback
*/
private static void OnStartMusic(String name, int loop) {
if (listener != null)
listener.OnStartMusic(name, loop);
}
/**
* Stop background music
* @param name
*/
private static void OnStopMusic(String name) {
if (listener != null)
listener.OnStopMusic(name);
}
/**
* Set background music volume
* @param volume Range: (0-255)
*/
private static void OnSetMusicVolume(int volume) {
if (listener != null)
listener.OnSetMusicVolume((int) (volume * 100.0 / 15.0));
}
}
```
## 3. 原生层
### 3.1 概述
原生层通过定义三种类型的任务将Java和C代码粘合在一起:
- 原生方法实现:这是原生接口类定义的原生Java方法的C实现,代码位于`jni_doom.c`文件中。
- 原始游戏更改:需要对原始Doom引擎进行轻微修改,以适应JNI粘合,这包括在正确的文件中插入对C到Java回调的调用。
- 移除无效依赖:必须移除原始代码中的无效依赖,例如,必须删除PC代码使用的原始Simple DirectMedia Layer (SDL)依赖。
### 3.2 原生方法实现
以下是Java原生方法签名及其在`jni_doom.c`中的C对应方法:
| Java方法 | C方法 |
| --- | --- |
| `static native int DoomMain(String[] argv)` | `JNIEXPORT jint JNICALL Java_doom_jni_Natives_DoomMain(JNIEnv * env, jclass class, jobjectArray jargv)` |
| `static native int keyEvent(int type, int key)` | `JNIEXPORT jint JNICALL Java_doom_jni_Natives_keyEvent(JNIEnv * env, jclass cls, jint type, jint key)` |
| `static native int motionEvent(int btn, int x, int y)` | `JNIEXPORT jint JNICALL Java_doom_jni_Natives_motionEvent(JNIEnv * env, jclass cls, jint btn, jint x, jint y)` |
在进行实现之前,必须使用`javah`命令生成所需的头文件和签名:
```sh
javah -jni -classpath PATH_TO_PROJECT_FOLDER/bin -d include doom.jni.Natives
```
需要注意的是,`javah`需要一个指向`bin`文件夹的类路径才能找到`doom.jni.Natives`类。使用`-d`选项将输出文件`doom_jn
0
0
复制全文
相关推荐









