存档

文章标签 ‘palib’

NDS开发Wiki翻译:第六天:设备相关的运算(2)

2006年6月29日 1 条评论

使用重力(实现跳跃等效果)


请务必已经读过定点数教学部分。。。代码很简单,你可以用于任何类型的游戏。。。你必须懂得一些定点数的运算,否则可能看不懂下面的东东。

"重力"的代码非常不错,因为它可以用在很多的游戏中,特别是类似于超级马里奥那种。。。它有一个基本原则:你的精灵(或马里奥或玩家或任何东西)有垂直速度的时候,"重力"代码能够给予这个速度以加速度。下面有3件事情:

  • 每次转身,速度根据它的加速度进行变化
  • 每次转身,精灵的位置根据它的速度而变化
  • 任何时候,如果你的精灵碰到地面(或在地面以下),它应该放到地面上来,并且速度应该归零。。。。

因为速度和加速度有很多值,我们也不希望给它们增加限制,我们将使用定点数。。。这就是你为什么要使用移位操作的原因(«8和»8这种操作 )。这个例子是演示一个小飞船上升,下降。你能够改变重力和启动力(相当于跳跃)来看看飞船的不同效果。

#define FLOOR (160<<8) // Floor y level

int main(void){

PA_Init(); //PAlib inits
PA_InitVBL();

PA_InitText(1, 0);

PA_OutputText(1, 2, 4, "Press A to take off !");
PA_OutputText(1, 2, 5, "Gravity change : Left/Right");
PA_OutputText(1, 2, 7, "Takeoff Speed change : Up/Down");

PA_LoadSpritePal(0, 0, (void*)sprite0_Pal)

PA_CreateSprite(0, 0, (void*)vaisseau_Sprite, OBJ_SIZE_32X32, 1, 0, 50, 50);

s32 gravity = 32; // change the gravity and check the result :)
s32 velocity_y = 0;
s32 spritey = FLOOR; // at the bottom
s32 takeoffspeed = 1000; // Takeoff speed…

while(1) // Infinite loops
{

takeoffspeed += (Pad.Held.Up – Pad.Held.Down)*8; // Change takeoff speed…
gravity += (Pad.Held.Right – Pad.Held.Left)*2; // Change gravity speed…

PA_OutputText(1, 4, 8, "Takeoff speed : %d ", takeoffspeed);
PA_OutputText(1, 4, 6, "Gravity : %d ", gravity);

if((spritey <= FLOOR) && Pad.Newpress.A) { // You can jump if not in the air…
velocity_y = -takeoffspeed; // Change the base speed to see the result…
}

// Moves all the time…
velocity_y += gravity; // Gravity…
spritey += velocity_y; // Speed…

if(spritey >= FLOOR) // Gets to the floor !
{
velocity_y = 0;
spritey = FLOOR;
}

PA_OutputText(1, 0, 0, "Y : %d \nVY : %d ", spritey, velocity_y);

if (spritey>>8 > -32) PA_SetSpriteY(0, 0, spritey>>8); // show if on screen
else PA_SetSpriteY(0, 0, 192);

PA_WaitForVBL();
}

return 0;
}

下面解释一下:

  • #define FLOOR (160«8)

原创的话痨如需转载请注明来源:http://blog.chenyi.me/blog/9266;转载的文章我会力所能及地注明来源如有疏漏请留言指正。

文章的脚注信息由WordPress的wp-posturl插件自动生成

分类: 编程 标签: ,

NDS开发Wiki翻译:第九天:文件系统(1)

2006年6月27日 没有评论

概览


首先,什么是文件系统?为什么你需要它?

好吧,文件系统没什么东西,它提供了另外向你的ROM增加文件的新方法。你现在已经知道了一种基本的简单的方法:编译期增加文件。。。。(译:这一点不会翻译了。。。略过。)实际上,还有另外一种方法,编译后增加文件。是的,这是可能的,而且有不小的便利:

  • 你能够突破4M空间的限制,因为这些文件并不存储在RAM中,而是在gba闪存卡的ROM中。。。。这同时也意味着这些文件只有在你使用gba闪存卡的时候才正常工作,而且不能在WMB和CF上工作(尽管我做了一个可以在上面正常工作的版本,但是有4MB大小的限制)(译:谁能告诉我gba闪存是什么?WMB和CF是什么?只能拿乔布斯的话安慰自己了:StayHungry,StayFoolish)
  • 因为你可以在编译后加入文件,这意味着最终用户可以加入文件。。。很多应用都会用到这个功能,举例来说,音乐播放器,图像浏览器。。。nDoS(译:nDoS应该是个使用PAlib开发出来的程序)使用文件系统来存储和读取OutLook/ThunderBird的通讯录。。。。我甚至在我现在的工程中使用文件系统来增加皮肤功能。你看到了,文件系统功能给予了最终用户定制你的软件的机会。

凡事都有坏处。。。

  • 不能工作于WMB和其他(前面提到过了,有4MB的限制)
  • 它的函数用起来真的比较痛苦,虽然我已经尽力简化他们了。

PAlib的文件系统被称为PAFS。。。。你也可以使用GBFS,这是PAlib早期版本中带的文件系统功能,目前的版本为了前向兼容还保留着。

需要的文件


好的,准备好了。。。开始!如你所猜,你需要一些文件让文件系统跑起来。。。我们看一下PAFS/PAFS这个例子,这是最简单的。。。打开目录,你就会看到你需要的文件。。。

  • PAFS.exe 是主文件。他的作用是将需要的文件加入你的ROM中,所以你需要他。。。
  • PAFS.log 是主文件生成的日志,记录生成过程中一些情况和数据…
  • PAFS.bat 是个重要的文件。用写字板打开这个文件,你会看到它包含了PAFS PAFS.ds.gba. 这些是什么?
    • PAFS是可执行文件,实际上就是指PAFS.exe…
    • PAFS.ds.gba 是指你的ROM,运行期可以动态加入文件的那个。。。你也可以命名为.nds文件。
    • 在这些的后面,你可以加入更多的参数,比如你文件存放的目录。如果你没有给定目录名称(就像这个例子一样),它将使用File/目录下的所有文件。

现在,你看到了Files目录了吗?在里面有很多文件文件。他们没有什么用,只是用来检查增加文件是否正常工作。如果正常,你应该能够在你的DS上看到这些文件(就像文本阅览器)。我们看看代码吧!

你同样可以在PAlibTools目录下发现一个PAFS.exe和.bat文件。

简单文件加载


在同一个例子里面,打开模板。。。这里是主函数里面应该包含的代码(除了经典的初始化函数以外)

PA_InitText(0, 0); // Initialise the text system on the bottom screen
PA_InitText(1, 0); // Initialise the text system on the top screen

PA_OutputText(1, 0, 0, "Loading PAFS…");

u32 FileNumber = PA_FSInit(); // Inits PA File System, and returns the number of files

PA_OutputText(1, 0, 1, " Files : %d ", FileNumber);

// For each file, we’ll draw it’s name on the bottom screen…
s32 i;
for (i = 0; i < FileNumber; i++){
PA_OutputText(0, 0, i, "%s.%s Size : %d, %d", PA_FSFile[i].Name, PA_FSFile[i].Ext, PA_FSFile[i].Length, PA_FSFile[i].FilePos);
}

// Output the first file text on the top screen, just to test…
PA_OutputText(1, 0, 5, "File 0 : %s", PA_PAFSFile(0));
PA_OutputText(1, 0, 7, "File 1 : %s", PA_PAFSFile(1));
PA_OutputText(1, 0, 9, "File 2 : %s", PA_PAFSFile(2));

// Infinite loop to keep the program running
while (1)
{
PA_WaitForVBL();
}

我们以2个文件初始化函数作为开始,每个屏幕一个,以及一个非常傻瓜的装载PAFS。。。。文本。。。。我不再细说了。

让我们看看重要的部分:

  • u32 FileNumber = PA_FSInit(); PA_FSInit 是初始化文件系统的函数。如果你不调用它,你就不能读任何文件。。。如果需要文件的信息,你只要遍历一遍所有的文件。。。 这个函数返回了文件的个数,我把它存在FileNumber这个变量里面。

然后是PA_OutputText(1, 0, 1, " Files : %d ", FileNumber);用文本显示文件的个数。

下面是有趣的部分:

s32 i;
for (i = 0; i < FileNumber; i++){
PA_OutputText(0, 0, i, "%s.%s Size : %d, %d", PA_FSFile[i].Name, PA_FSFile[i].Ext, PA_FSFile[i].Length, PA_FSFile[i].FilePos);
}

这些代码起什么作用呢?对每个文件(按照文件顺序,0号文件,1号。。。),显示出文件的名称,文件名后缀,文件长度,文件在文件系统中的位置。你不会使用最后2个信息,但是文件名称和后缀是非常有用的。

  • 名称,这样你才能允许用户指定装载哪个文件。。。
  • 扩展名,这样你就能知道文件是什么类型。。。

就像你看到的那样,获取这些信息非常简单。他们被包含在PA_FSFile结构数组里面。PA_FSFile[n]是第n个文件(注意:编号是从0开始,不是从1),.Name, .Ext, .Length, .FilePos是这个结构的值。

你看到的最后一件事情是PA_OutputText(1, 0, 5, "File 0 : %s", PA_PAFSFile(0));. 非常重要的东西,张大你的眼睛!(译:我觉得这个作者是不是老师出身?)

  • PA_PAFSFile(File Number) 是指向真正文件的指针,参数Number是文件的编号。做关于这个文件的任何操作,我们都要使用这个指针。。。它就像文件名称一样唯一标示了这个文件,在PAFS中你就得学会用这个。。。

这个例子里,我们使用在文本命令里面使用指针。。。为什么?很简单因为我们增加的这些文件是文本文件,所以我们能够把文本显示在屏幕上。。。如果你试图显示其他类型(比如音乐文件)的文件到屏幕上,你将看到一屏幕的天书。。。

好了,现在我们看过了代码,你编译下。。。在把你的ROM放到你的闪存卡之前,别忘了加入文件!要做到这个,运行一下PAFS.bat,它会把所有Files目录下的文件搞到你的ROM里面。烧录,测试,你现在可以在你的DS屏幕上看到你的文本啦!(译:看起来实现读书功能实在太简单了,比我原来设想的要简单很多)

哈哈,程序已经完成了!但是教程还没有结束。还有很多东西值得一看,比如说读取目录结构(是的,PAFS支持目录!)或者在WMB和CF卡上使用PAFS。。。。还有一些例子演示了如果从文件装载图片和音乐,你可以在对应的目录下找到这些例子。

(译:待续)

原创的话痨如需转载请注明来源:http://blog.chenyi.me/blog/9261;转载的文章我会力所能及地注明来源如有疏漏请留言指正。

文章的脚注信息由WordPress的wp-posturl插件自动生成

分类: 编程 标签: ,

NDS开发Wiki翻译:第八天:硬件

2006年6月27日 没有评论

这里我们将介绍所有PAlib中特别针对DS的函数,比如:关机,合起状态,用户信息,时钟。。。

时间和日期


获取时间和日期非常简单,简直不用学习!例子在PAlibExamples/Other/Date_Time,只有2行

PA_OutputText(1, 2, 10, "%02d/%02d/%02d", PA_RTC.Day, PA_RTC.Month, PA_RTC.Year); // Date

PA_OutputText(1, 2, 12, "%02d:%02d %02d seconds", PA_RTC.Hour, PA_RTC.Minutes, PA_RTC.Seconds); // Time

你不用关心文本显示的函数,只用关心里面的变量。。。PA_RTC是一个每帧刷新的结构,包含了当前日期和时间。PA_TRC有如下变量:

  • PA_RTC.Day, 日期,范围1到31
  • PA_RTC.Month, 月份,范围1到12
  • PA_RTC.Year, 年份,从 00 (相当于 2000) 到 99 (相当于2099)
  • PA_RTC.Hour, 小时,0到23
  • PA_RTC.Minutes, 分钟,0到59
  • PA_RTC.Seconds, 秒,0到59

不用再废话了吧。。一清二楚。

用户信息


(译:用户信息就是第一次开机的时候输入的生日啊,喜欢的颜色啊,等等信息)

用户信息并不难获取!看看PAlibExamples/Other/UserInfo这个例子。。。

用户信息包含在PA_UserInfo结构中,包含以下变量:

  • PA_UserInfo.Name, 用户名称
  • PA_UserInfo.BdayDay, 生日,几号
  • PA_UserInfo.BdayMonth, 生日,几月
  • PA_UserInfo.Language, 语言,0表示日文,1表示英语,2表示法文(译:好奇IDSL里面是多少)
  • PA_UserInfo.Message, 用户输入的信息
  • PA_UserInfo.AlarmHour, DS闹钟,小时(0到23)
    • PA_UserInfo.AlarmMinute, DS闹钟,分钟
    • PA_UserInfo.Color, 用户喜欢的颜色

就这些了。。。

合屏暂停


(译:DS正在玩的时候,把上下屏幕闭合,游戏就会暂停)

我猜你已经发现了如果你把上下屏闭合,游戏会自动暂停!至少在商业游戏中会这样。。。。你不想在你的游戏中加入这一特性吗?很容易!只要加一行函数就行了!

PA_CheckLid(); 检测是否上下屏闭合的函数。。。。如果上下屏已经闭合就会返回1,否则就返回0

我们应该把这个函数放在哪里?放在PA_WaitForVBL();函数之前。你可以试试Other/CheckLid这个例子,不过模拟器上可没有盖子让你合起来。

屏幕背景光


屏幕背景光!你能随意调节它。。。。

看看这个例子Other/ScreenLight

if (Pad.Newpress.A) PA_SetScreenLight(0, 1); // Turn on bottom light

if (Pad.Newpress.B) PA_SetScreenLight(0, 0);

if (Pad.Newpress.X) PA_SetScreenLight(1, 1);// Turn on top light

if (Pad.Newpress.Y) PA_SetScreenLight(1, 0);

超级简单的函数。。。。


原创的话痨如需转载请注明来源:http://blog.chenyi.me/blog/9256;转载的文章我会力所能及地注明来源如有疏漏请留言指正。

文章的脚注信息由WordPress的wp-posturl插件自动生成

分类: 编程 标签: ,

NDS开发Wiki翻译:第七天:声音

2006年6月26日 没有评论

今天,我们谈一点DS的声音播放。。。首先,你要知道PAlib提供了两种声音输出的方式:

  • raw格式,就像播放wav文件一样。对于声效很有用,如果作为音乐就太占地方了。
  • mod播放(鸣谢Deku!),作为音乐回放非常好,因为他占用rom空间比较笑。。。

Raw声音文件


DS不能播放Wav文件(也不能放mp3和ogg文件),因此你必须先转化成raw格式,再播放。。。

转化Wav文件到Raw格式


我个人喜欢使用switch工具,http://www.nch.com.au/switch/,因为它工作得不错,而且容易使用,但是Kleevah会向你们推荐Audacity, http://audacity.sourceforge.net/,还有一些人喜欢使用Sox(命令行方式)。我将在教程中使用Switch工具,如果你们中有人使用其他工具得话,我们可以增加相关工具的教学。。。

下载安装Switch以后,打开它,你会看到:

http://www.palib.info/wiki/lib/exe/fetch.php?cache=cache&media=http%3A%2F%2Fwww.palib.info%2FScreens%2FSwitch1.png

现在,点一下"增加文件"或"增加目录"按钮,把你要转化的文件加到列表里面。。。增加完成以后,在界面左下方修改输出格式为raw格式,并修改编码设置如下:

http://www.palib.info/wiki/lib/exe/fetch.php?cache=cache&media=http%3A%2F%2Fwww.palib.info%2FScreens%2FSwitch2.png

如果你喜欢的话,也可以保留立体声输出,速率也可以设置为其他值。。。但是你必须把格式设置成"8 bit signed",否则就玩不起来了。。。我推荐使用图中的参数,它可以帮助你获得较小的文件以及不错的音质。

设置一下你的输出路径,然后点一下"转化"按钮,稍等一会,你就得到你要的raw文件了!

播放RAW文件


现在你已经有了raw文件,把他们放到你工程的data目录下。如果没有这个目录,那就创建它。。。我们使用Sound/Sound这个例子来讲解。。。

可以看到代码很容易理解:

#include <PA9.h> // Include for PA_Lib

#include "saberoff_raw.h" // Include the sound (found in the data folder in .raw format)

// Function: main()

int main(int argc, char ** argv)

{

PA_Init(); // Initializes PA_Lib

PA_InitVBL(); // Initializes a standard VBL

PA_InitText(0, 0);

PA_InitSound(); // Init the sound system

PA_OutputSimpleText(0, 6, 10, "Press A to play the sound");

// Infinite loop to keep the program running

while (1)

{ // Play the sound if A is pressed…

if (Pad.Newpress.A) PA_PlaySimpleSound(0, saberoff_raw);

PA_WaitForVBL();

}

return 1;

}

只需要做以下的步骤:

  1. #include "saberoff_raw.h" include 你的raw文件,告诉编译器他们在哪里。。。。就是 nameoftherawfile.h 文件,放到你的include目录下。。。
  2. PA_InitSound(); 初始化PAlib的声音系统(raw和mod都适用),设置确认raw文件类型为11025采样率和8 bit signed格式。。。
  3. PA_PlaySimpleSound(channel, soundfile); 这个函数很容易使用,选择一个声道 (0-7) 和一个声音文件。。。同时可以播放8个声道(缺省情况下,另外8个声道(译:共有16个声道)是保留给mod播放器的,但是这个也可以设置,后面我们再具体谈)

编译,下载到你的烧录卡中,按一下A键享受你的音乐吧!

Mod文件


Mod文件用起来非常酷,容易使用,占用空间少,效果不错。。。。唯一不好的地方就是不容易创建,比较好的办法是去网络上下载。。。我个人推荐去http://www.modarchive.com/http://www.exotica.org.uk/frames.htmlftp://ftp.modland.com/pub/modules/这几个地方下载。可惜只有mod文件,没有转化工具。。。

现在,让我们开始我们的例子吧:Sound/ModPlayer !

首先,把mod文件放到data目录下。。。

让我们看代码:

// Includes

#include <PA9.h> // Include for PA_Lib

#include "modfile_mod.h" // Include the mod file (the .mod file is in the data directory)

// Function: main()

int main(int argc, char ** argv){

PA_Init(); // PA Init…

PA_InitVBL(); // VBL Init…

PA_InitSound(); // Sound Init, for the mod player…

PA_PlayMod(modfile_mod); // Play a given mod

while(1){ // Infinite loop

PA_WaitForVBL();

}

return 0;

} // End of main()

只需要知道3件事情!

  1. #include "modfile_mod.h" 必须放在你的文件头部,在PAlib include之后。。。如果你不加,程序就不知道你的mod文件在哪里。
  2. PA_InitSound(); 是初始化的函数,必须在播放之前调用它。
  3. PA_PlayMod(modfile); 使用这个函数播放mod文件。

现在,最后一件事情:我们看看有哪些限制。。。你的mod文件最多只能有16个声道,我推荐使用8个,你可以使用剩下的8个播放音效。

原创的话痨如需转载请注明来源:http://blog.chenyi.me/blog/9255;转载的文章我会力所能及地注明来源如有疏漏请留言指正。

文章的脚注信息由WordPress的wp-posturl插件自动生成

分类: 编程 标签: ,

NDS开发Wiki翻译:第六天:设备相关的运算(1)

2006年6月24日 没有评论

目标有2个:

  • 让代码运行得更快一点
  • 能处理特性(抛物线,重力,碰撞)

随机数


经常会用到随机数。比如在玩彩票的时候(译:用随机数来决定谁中奖),设置人工智能的时候(译:存在多种选择的时候随机选择一个),升级的时候能力增加的时候(译:比如宠物升级随机增加属性)。我知道你在想什么,"我如何获得随机数呢"。

PA_Rand函数


直接用PA_Rand函数来获得随机数。

u32 num = PA_Rand();

产生一个随机数,可能会很大,因此某些情况下不实用。

PA_RandMax(max)


PAlib在0.73a版本中新增了这个函数。

如果我们需要在0和4之间随机获得一个数字,这样用:

u32 max = 4;

u32 num = PA_RandMax(max);

PA_RandMinMax(min,max)


如果我们需要在1和4之间随机获得一个数字,这样用:

u32 min = 1;

u32 max = 4;

u32 num = PA_RandMinMax(min,max);

PA_InitRand()


最后一件要说的事情:如果你已经测试了一下程序,你就会发现每次生成的随机数都是固定的。。。为什么呢?因为计算机是不能真正产生随机数的,它只是用某种算法来模拟随机效果。。。为了对付这种糟糕的状况,PA_InitRand()函数就有用了。把它放在游戏主循环的前面,只要运行一次就够了。。。。一旦运行,它将以当前时间和日期初始化随机函数。如果你开始游戏的时间和日期都一样,那么依然会得到同样的随机数序列。。。但这个情况实际很难碰到。。

定点数运算(Fixed Point Math )


这里将显示如何使用定点数(不使用浮点数)来管理坐标。这样可以加快你程序的运行。。。

理论


DS(或者GBA)处理浮点类型变量很慢。。。但是,使用小数有时也是不可避免的,比如你希望让你的精灵移动的时候速度小于1精灵/帧,比如0.5。。。那么你该怎么办?

我的解决方法是,使用32位变量(s32),把其中8位保留给小数部分,另外24位作为整数部分。。。

8位可以表示0~255,这意味着可以达到1/256的精度,大部分情况下都足够了。。。而另外24位,可以表示2的24次方,也就是0~16777215,也够了。。。

在这种处理下,256实际表示1,512表示2,1024表示4。。。128表示0.5。。。196表示0.75。。。。明白了吗?

下面看看如何使用他们

基本的例子


下面是代码,演示了使用定点数移动坐标比使用浮点数快得多。。。

s32 speed1 = 256; // first speed…0

PA_OutputText(0, 18, 2, " 1 pixel/frame");

s32 speed2 = 128;

PA_OutputText(0, 18, 10, "0.5 pixel/frame");

s32 speed3 = 64;

PA_OutputText(0, 18, 18, "0.25 pixel/frame");

PA_LoadSpritePal(0, 0, (void*)sprite0_Pal); // Palette name
PA_CreateSprite(0, 0, (void*)vaisseau_Sprite, OBJ_SIZE_32X32, 1, 0, 0, 0);

PA_CreateSprite(0, 1, (void*)vaisseau_Sprite, OBJ_SIZE_32X32, 1, 0, 0, 64);

PA_CreateSprite(0, 2, (void*)vaisseau_Sprite, OBJ_SIZE_32X32, 1, 0, 0, 128);

// all sprites stick to the left

s32 spritex1 = 0; s32 spritex2 = 0; s32 spritex3 = 0;

while(1) // Infinite loops

{

// Move all the sprites by their corresponding speed

spritex1 += speed1; spritex2 += speed2; spritex3 += speed3;

// Positionne all the sprites, >>8 to return to normal position

PA_SetSpriteX(0, 0, spritex1>>8);

PA_SetSpriteX(0, 1, spritex2>>8);

PA_SetSpriteX(0, 2, spritex3>>8);

PA_WaitForVBL();

}

我只对这段代码重要的部分进行了注释,类似的部分我就没有反复地注释了。。。

  • s32 speed1 = 256; 就像理论部分提过的那样,实际上256对应于1;
  • s32 speed2 = 128; 128对应于0.5
  • s32 speed2 = 64; 64对应于0.25

顺便说一下,为什么我们使用s32类型?s32的"s"表示它是带符号数,因为我们可能需要处理负数。s32的"32"是因为我们需要32位数:8位给小数,24位给整数部分。如果使用s16,那么就不怎么够用了。

很容易!现在我看看如何改变精灵的坐标。如下:

  • spritex1 += speed1; 这里只使用了加法。没有浮点运算。

现在,我们看看最关键的技巧。

  • PA_SetSpriteX(0, 0, spritex1»8); 为什么是>>8?>>8是向右移位8位,效果相当于除以256,但是移位操作比除法快多了。

(译:如果学过计算机基础,根本不需要解释;如果没有学过,解释也是白搭,跳过2行不翻译了。)

(译:待续)

原创的话痨如需转载请注明来源:http://blog.chenyi.me/blog/9253;转载的文章我会力所能及地注明来源如有疏漏请留言指正。

文章的脚注信息由WordPress的wp-posturl插件自动生成

分类: 编程 标签: ,