在学会架设开发环境,编写第一个程序,领略一些编程技巧,并且涉猎列一些经典程序之后,接下来我们开始第五课,你不要想这里有什么会让你感到惊讶的,这只是通过学习一些高级的概念来增强我们的编程技巧。
高级,这个只是个人看法,我们其实只是增强我们的C基础.以便于将来在真正高级的应用程序上编写程序。学过这节课后,你应该掌握如何"超频"到 psp真正的333MHz的速度。能够将文字讯息显示到屏幕上(图片文本,不是我们之前用的debug文本),并且了解在psp上的颜色工作是如何进行的。
我们所要创建的程序是背景转换,因为我们需要允许用户在背景上选择颜色,所以我们要转换它,并且将转换的颜色元素显示在屏幕上。
首先是关于一些背景知识,这里存在一个关于psp 的“超频”的普遍的误解。因为本来观念的原因,人们很容易联想到"超频"会伤害和损坏硬件。说"超频"psp对硬件是危险的,简直是无稽之谈。这种情况并 不只是在psp上出现。实际上,psp从出厂后就已经降频。为什么sony这么做,,有几个说法。一种是sony因为它会非常快的消耗掉电池(对我们来说 到是无关紧要的,运转UMD比从记忆棒上读取存储需要用掉更大的能量,所以我们在电池寿命的方面已经有一个很大的优势)。另一种说法是sony希望开发者 去编写更新的代码。而更多人认同第三种说法(跟我本人的观点也基本相符)是他们希望将来可以通过允许开发者在一些要点上可以用到全速,来推进游戏程序编程 的发展。总之,psp正常的运行速度是222MHz,如果你需要更大的力量来运行你的程序,"超频"是一个选择。而我们这里所要编辑的程序,将用不到我上 面所说得那些,但是我想这是一个很好地时机来介绍这个概念。
现在,让我们开始我们的程序,你需要下载另外一个压缩文件,你点击”here”可以获得链接。释放这个压缩文件,你需要在文件夹里面创建一个main.c文件。
#include <pspkernel.h>
#include <pspdisplay.h>
#include <pspctrl.h>
#include <stdio.h>
#include <psppower.h>
#include "graphics.h"
假设你已经看过前面几课,这里就没有什么东西是你看不明白的。包括唯一对你有点陌生的"psppower.h"。这个文件包含了我们可以用来快速改变psp时钟的的函数。
列出一些常见的代码:
PSP_MODULE_INFO("Background Changer", 0, 1, 1);
#define RGB(r, g, b) ((r)|((g)<<8)|((b)<<16))
记得在第二课我们只是一笔带过这些代码而没有对他们究竟是做什么用加以解释。现在是时候让你们知道了,第一个参数是你的程序标识符,基本上是你程序的名字。第二个参数允许你传入属性。因为你的大多数程序,你将只是用到一个0,但是如果你编辑一个内核的应用模式,你将需要把它转换成"0x1000",第三个参数是掌管转换(翻译),第四个是辅助转换(翻译)。他们是用来给你的程序的转换数据的。
标准的回叫:
/* Exit callback */
int exit_callback(int arg1, int arg2, void *common) {
sceKernelExitGame();
return 0;
}
/* Callback thread */
int CallbackThread(SceSize args, void *argp) {
int cbid;
cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
sceKernelRegisterExitCallback(cbid);
sceKernelSleepThreadCB();
return 0;
}
/* Sets up the callback thread and returns its thread id */
int SetupCallbacks(void) {
int thid = 0;
thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
if(thid >= 0) {
sceKernelStartThread(thid, 0, 0);
}
return thid;
}
现在我们把它的骨骼架设起来了,我们应该让我们的程序有血有肉,应用main()主函数。
int main(void) {
scePowerSetClockFrequency(333, 333, 166);
这个数据行是用来设置时钟的频率(丛函数名字就可以很显然地看出来),你可能很奇怪为什么有三个参数,我想问,是否这三个中只有一个是处理器呢?答案是错误的,psp 实际上有三个处理器。第一个是CPU(中央处理器),你得几乎所有的程序都需要它来处理。第二个参数是媒体播放器,第三个我该怎么说呢,你可以在你的处理 器上运行MIPS 汇编代码。媒体播放器的速度是333 MHz,第三个参数控制图形中心,这就是人们常说得"GPU","GPU"的运行速度在166 MHz,默认的速度是"222, 222, 111."(如果你自己没有改变它的话)。
接下来,我们把它显示在屏幕上,跟我们上一课所作的一样:
SetupCallbacks();
initGraphics();
因为我们的输入需要要被接受,回忆一下第三课所讲的,我们需要定义一个SceCtrlData类型的变量,我们定义它为:"pad."
SceCtrlData pad;
我们再设立两个变量和一个数组:
int i;
int selComponent = 0;
char filler[10];
整数"i,"将被运用到我们接下来的一个循环,字符数组我们把它用作一个字符串来输出一些这个程序之后信息。"selComponent"是我们用作纪录用户是使用哪一种颜色。
下面我们要声明并且初始化的三个变量是用来存储我们的背景信息的。一种颜色是有三种成分构成的:红、绿、蓝。用32 位来表示,每一种颜色在的最大值是255。对每一种颜色,零是黑色,255是白色。1~255之间的就是复合色。有时候,你会看到用16位值来描绘颜色。 16位是基于16来计算的系统。他是从0运行到F(F就是16)。所以,在32位的系统下,FF=255(即16*16,减1的原因是因为你是从零开始计 算的)所以,白色就是"FFFFFF"(255红,255兰,255绿)。总之,我们用这三种变量来存储背景颜色信息:
int bgR = 0;
int bgG = 0;
int bgB = 0;
目前我们的背景是黑色,尽管这样,它也是存储了信息以便于我们可以熟练的运用它。我们实际上并不需要设置背景的颜色为黑色,那需要我们改变这些变量的值。
因为我们需要用"graphics.h"文件而不是用pspDebugScreenPrintf来显示信息,我们需要告诉我们的程序我们要用哪些颜色。
Color highlightColor = RGB(200, 200, 200);
Color dimmedColor = RGB(100, 100, 100);
Color shadowColorH = RGB(55, 55, 55 );
Color shadowColorD = RGB(55, 55, 55 );
首先,我要解释这些数据行在做什么,他们在运用RGB宏,这个我们在第四课已经做以解释—他们把颜色成分转换为单色。"Color" 数据类型在"graphics.c"的包含文件中有定义,你也可以通过u32(一个32位整型)在一些程序中来控制一种颜色。
这 节课用到四个变量是因为我们希望加亮这些用户通常转换的颜色成分。所以我们有两个主颜色,一种颜色是为了高亮文字,另一种是为了暗淡的文字。同样,我们有 一个动态背景(意味着背景是变幻的)我们将用到一个影子,它可以帮助我们的文档从背景里面突出出来,我试过很多颜色,随意使用这些颜色从而找到你最喜欢的 一种。
继续我们的游戏循环:
while(1) {
sceCtrlReadBufferPositive(&pad, 1);
一个标准的无限while循环和手柄控制信息读入的函数
现在我们掌握了手柄控制信息读入的函数,我们所要做的就是用上下键选择颜色。我们用左右键来调节那些颜色。
if(pad.Buttons & PSP_CTRL_UP) {
if(selComponent > 0) {
selComponent--;
}
for(i=0; i<10; i++) {
sceDisplayWaitVblankStart();
}
} else if(pad.Buttons & PSP_CTRL_DOWN) {
if(selComponent < 2) {
selComponent++;
}
for(i=0; i<10; i++) {
sceDisplayWaitVblankStart();
}
}
代码块的应用是前半部分是这样得,如果点击上键,他就会减少变量(因为我们想要从上边开始运行,0是最上边的颜色,1是中间颜色,2是末端颜色)如果按下向下键,他就会增加变量。当某个按钮按下的时候For循环的作用是用来停顿程序一会,这样就大大简化了用户的操作。
下面的代码块是控制左右键的:
if(pad.Buttons & PSP_CTRL_RIGHT) {
switch(selComponent) {
case 0:
bgR++;
break;
case 1:
bgG++;
break;
case 2:
bgB++;
break;
default:
//SHOULD NEVER EXECUTE
break;
}
} else if(pad.Buttons & PSP_CTRL_LEFT) {
switch(selComponent) {
case 0:
bgR--;
break;
case 1:
bgG--;
break;
case 2:
bgB--;
break;
default:
//SHOULD NEVER EXECUTE
break;
}
}
当你看到这些代码,你可能会自问: switch是什么意思?这个问题非常简单,这个比较像if/else。它用到一个变量,"switch(你的变量)”,举例说明,在我们的程序中,"selComponent"变量被限定为:0、1、2,
如果"selComponent"等于0,我们的变量"bgR"将增加1,如果它等于1,"bgG"也增加1,如果它等于2,"bgB"也增加1,这时break语句就会退出转换结构。但是如果他找不到匹配的数值,那么程序将会运行默认的数值。
这样,我们就有了允许我们修改颜色的代码,通过改变变量来改变颜色,下一步所要做的是在屏幕上显示的程序。我们的用户界面(UI)由文本组成,它可以告诉用户,界面的颜色成分和背景。
我们说过,颜色的值是0-255,因为我们需要确认当用户运用它的时候取值不能超过这个范围,所以我们要运用一连串的if/else语句来限定取值范围。
if(bgR < 0) {
bgR = 0;
} else if(bgR > 255) {
bgR = 255;
}
if(bgG < 0) {
bgG = 0;
} else if(bgG > 255) {
bgG = 255;
}
if(bgB < 0) {
bgB = 0;
} else if(bgB > 255) {
bgB = 255;
}
这是一个漂亮的自我说明,如果颜色值超过这个范围,我们把它设置成范围的最大和最小值。
现在根据我们的变量来填充我们的背景颜色。
fillScreenRect(RGB(bgR, bgG, bgB), 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
"fillScreenRect()"是被"graphics.c."定义的一个函数。它是构成了矩形屏幕的特定颜色的一部分,他有五个参数,第一个是矩形的颜色,第二个是矩形的x点,第三个是矩形的y点,第四个是矩形的宽度。第五是矩形的高度。
我们在背景上写文本需要我们键入:
sprintf(filler, " RED: %i", bgR);
if(selComponent == 0) {
printTextScreen(11, 10, filler, shadowColorH);
printTextScreen(10, 10, filler, highlightColor);
} else {
printTextScreen(11, 10, filler, shadowColorD);
printTextScreen(10, 10, filler, dimmedColor);
}
我们在第四课学过"sprintf ()"函数,那时我们对"printf()"在字符串中的作用做以解析,这次,我们用到一个变量,我们需要同时显示颜色(红色)和值(bgR)跟第三课的 方法相同,我们输入一个整数,(用"%i"),一旦颜色成分被选定,我们就应用来自"graphics.c"的"printTextScreen()"函 数来把我们的字符串打印到屏幕上。这个函数分为四个参数,第一个是x点,第二个是y点,第三个是字符串,第四个是颜色。用if/else语句的理由是因为 我们想要在不同的被选定的颜色中显示文本。我们也要在文本的下面输入阴影。
同样的方法应用在另外的两种颜色上:
sprintf(filler, "GREEN: %i", bgG);
if(selComponent == 1) {
printTextScreen(11, 20, filler, shadowColorH);
printTextScreen(10, 20, filler, highlightColor);
} else {
printTextScreen(11, 20, filler, shadowColorD);
printTextScreen(10, 20, filler, dimmedColor);
}
sprintf(filler, " BLUE: %i", bgB);
if(selComponent == 2) {
printTextScreen(11, 30, filler, shadowColorH);
printTextScreen(10, 30, filler, highlightColor);
} else {
printTextScreen(11, 30, filler, shadowColorD);
printTextScreen(10, 30, filler, dimmedColor);
}
我们点击屏幕增加一小段的暂停来控制我们程序的速度(我们不希望它运行得太快)最后,修改我们的程序。
flipScreen();
for(i=0; i<1; i++) {
sceDisplayWaitVblankStart();
}
}
return 0;
}
现在,增加一个Makefile,为你的编译做准备。
TARGET = BackgroundChanger
OBJS = main.o graphics.o framebuffer.o
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS = $(CFLAGS)
LIBDIR =
LIBS = -lpspgu -lpsppower -lpng -lz -lm
LDFLAGS =
EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = Background Changer
PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak