PSPChina's Archiver

dr_watson 发表于 2005-10-17 14:21

[原創]學習PSP遊戲制作實況記錄 (0-5篇) - 10月24日新加第5篇: 制造噪音(50樓, 總

------------------------------------------------------------------------
[b]總目錄:

[url=http://bbs.pspchina.net/viewthread.php?tid=33513&fpage=1][b]>第0篇: 由零開始!(1樓)[/b][/url]
[url=http://bbs.pspchina.net/viewthread.php?tid=33513&fpage=3&toread=&page=3]>第1篇: 載入和顯示PNG檔(20樓)[/url]
[url=http://bbs.pspchina.net/viewthread.php?tid=33513&fpage=3&toread=&page=3]>第2篇: 用家操控和動畫(25樓)[/url]
[url=http://bbs.pspchina.net/viewthread.php?tid=33513&fpage=&toread=1&page=4]>第3篇: 透明色的處理(31樓)[/url]
[url=http://bbs.pspchina.net/viewthread.php?tid=33513&fpage=&toread=1&page=4]>第4篇: 雙緩衝區和背景捲動(37樓)[/url]
[url=http://bbs.pspchina.net/viewthread.php?tid=33513&page=6]>第5篇: 制造噪音(50樓)[/url]
------------------------------------------------------------------------
補充篇:

[url=http://bbs.pspchina.net/viewthread.php?tid=33513]關於VRAM和sceDisplayWaitVblankStart()(4樓)[/url]
[url=http://bbs.pspchina.net/viewthread.php?tid=33513&fpage=&toread=&page=3]利用cgywin安裝libpng(23樓)[/url]
[url=http://bbs.pspchina.net/viewthread.php?tid=33513&fpage=&toread=&page=4]關於OR, AND 和 XOR(30樓)[/url]

------------------------------------------------------------------------
Bugs:

[url=http://bbs.pspchina.net/viewthread.php?tid=33513&page=4&fpage=1]集中於36樓[/url]

2005.10.20 - DrawImage() 找到一個蟲
2005.10.22 - DrawImage(), DrawImageRect() 又找到一個蟲
[/b]
------------------------------------------------------------------------


經過大大小小的"波折"才裝好了PSPSDK, 現在, 我終於可以開始干我本來想干的事了: 開發PSP遊戲.

拿到PSP到現在才一個多星期, 對於它的認識當然很少, 所以一切差不多是從零開始, 忽發奇想, 何不把整個學習過程記錄在案, 借此, 可以跟有興趣自制PSP遊戲的朋友互相交流學習, 有錯或不妥當的地方, 也可望能得到高手們的指點.

也不說太多的門面話了, 開始第一篇:

[b]由零開始![/b]

當我知道PSP可以直接讀寫VRAM(VIDEO RAM), 我心中不由一喜, 要知道可以直接讀寫VRAM, 以遊戲編程的角度來說, 可是拿到了一把"屠龍寶刀", 只要我們有相應的知識, 2D/3D/特效等等都可以弄出來.

當然, 話分兩頭說, 要是只靠讀寫VRAM, 遊戲能不能達到到圓滑暢快的感覺, 是很依賴PSP處理器和VRAM的速度和編程功力了. 還好, PSP還有3D加速晶片和OPENGL ES可供選擇.

但我們應該先學爬再學跑, 所以我會從最基本開始, 這一篇, 我會說一下在螢幕上畫點和畫四方形, 下面是本篇程式的截圖:

[img]http://www.khors.com/pspchina/game00/game00.png[/img]

本篇尾有原始碼和1.5的執行檔附件, 大家也可以在這里下載:

[url=http://www.khors.com/pspchina/game00/game00_src.zip]source[/url]
[url=http://www.khors.com/pspchina/game00/game00_1.50.zip]1.5執行檔[/url]

大家看到本篇的原始碼, 就會知道它只是由HELLO WORLD改成, 所以我只是著重說明一下在螢幕上畫點和畫四方形的部份.

[quote]
#define SCREEN_WIDTH               480
#define SCREEN_HEIGHT               272

#define       SCAN_LINE_SIZE               512          // 512 units (either 16bits or 32bits per scanline

#define ARGB(a, r, g, b)       (a<<24|b<<16|g<<8|r)
[/quote]

先看一下開始時候的一些DEFINES, PSP的螢幕大小是480X272, 大家可能都已知道了. 至於這個SCAN_LINE_SIZE則是說明了, 雖然螢幕的橫度是480, 到實際上, 每一條綫, 是512個單位, 就是說每條綫的右面, 有32個單位是沒有用到的, 或是有些其他用圖.

當螢幕設定為32bit一個像素時, 顏色的排列是ABGR, 即透明度, 藍, 綠, 紅. 和我們平時用的ARGB有點不同, 所以用這個ARGB MACRO方便一下.

[quote]
// VRAM actually starts from 0x04000000 but need to OR with 0x40000000 to prevent
// unpredictable behaviours due to caching
u32* pVRAM = (u32*)(0x04000000+0x40000000);      
[/quote]

pVRAM 就是VRAM的地址了, 它本身應該是0x4000000, 但根據
[url=http://wiki.pspdev.org/psp:memory_map]pspdev wiki[/url] 所言,  一定要OR 0x40000000才可直接運用, 要不然會有意想不到的後果!

有了VRAM地址, 我們就可以畫點了:

[quote]
// Plot a single pixel on screen
void Plot(int x, int y, u32 color)
{
       u32* p = pVRAM + y * SCAN_LINE_SIZE + x;
       *p = color;
}
[/quote]

很是方便呢! 注意:上面已提到, 每一條綫是512個單位, 所以我們在計算每條綫的地址時, 是乘以512而不是480!

明白了畫點, 畫四方形也就很容易了:

[quote]
// Fill a rectangular area with a specific color
void FillRect(int x, int y, int width, int height, u32 color)
{
       // 計算第一個像素地址
       u32* p = pVRAM + y * SCAN_LINE_SIZE + x;
       int i, j;
       for (j=0;j<height;j++)
       {
              for (i=0;i<width;i++)       // 畫一條綫
              {
                     *(p+i) = color;
              }
              
              p += SCAN_LINE_SIZE;       // 把地址移到下一行
       }
}
[/quote]

要是大家有注意, 會看到程式的主LOOP最後, 有個:

[quote]
sceDisplayWaitVblankStart();
[/quote]

根據一些網上資料所說, 要是我們的主LOOP是一直的走, 而中間沒有一丁點停頓, 當用家按下"HOME"想把程式中止, PSP的KERNEL也會接收不到訊息, 除非我們另有"出口", 不然用家可要拔電池了!

本篇到此也該完結了, 但忽然想到用作示範的話, 有個方便的截圖功能會比較好, 於是加了從[url=http://forums.qj.net/showthread.php?t=19422]QJ FORUM[/url] 找到的截圖功能.

[quote]

// Taken from: [url]http://forums.qj.net/showthread.php?t=19422[/url]
// Original programmer: Yeldarb
void WriteByte(int fd, unsigned char data)
{
       sceIoWrite(fd, &data, 1);
}


void ScreenShot()
{
       const char tgaHeader[] = {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0};
       const int width = SCREEN_WIDTH;
       const int lineWidth = SCAN_LINE_SIZE;
       const int height = SCREEN_HEIGHT;
       unsigned char lineBuffer[width*4];
       u32* vram = pVRAM;
       int x, y;
       int fd = sceIoOpen("ms0:/screenshot.tga", PSP_O_CREAT | PSP_O_TRUNC | PSP_O_WRONLY, 0777);
       if (!fd) return;
       sceIoWrite(fd, tgaHeader, sizeof(tgaHeader));
       WriteByte(fd, width & 0xff);
       WriteByte(fd, width >> 8);
       WriteByte(fd, height & 0xff);
       WriteByte(fd, height >> 8);
       WriteByte(fd, 24);
       WriteByte(fd, 0);
       for (y = height - 1; y >= 0; y--) {
              for (x = 0; x < width; x++) {
                     u32 color = vram[y * lineWidth + x];
                     unsigned char red = color & 0xff;
                     unsigned char green = (color >> 8) & 0xff;
                     unsigned char blue = (color >> 16) & 0xff;
                     lineBuffer[3*x] = blue;
                     lineBuffer[3*x+1] = green;
                     lineBuffer[3*x+2] = red;
              }
              sceIoWrite(fd, lineBuffer, width * 3);
       }
       sceIoClose(fd);
}
[/quote]

當用戶按"HOME"中止, 本篇程式就會在MEMORY STICK 的根目錄, 寫入一個叫"screenshot.tga"的圖檔.

要是大家不想每次都浪費時間寫入這個檔, 可以把以下的句子刪掉:

[quote]
ScreenShot();
[/quote]

[[i] 本帖最后由 dr_watson 于 2006-5-12 20:33 编辑 [/i]]

china0008 发表于 2005-10-17 14:31

好啊。收藏保存一下。大哥继续写哈。

china0008 发表于 2005-10-17 14:32

大哥可以把您使用的  编译环境平台的  架设写一下吗?我到现在没有搞定环境啊。帮帮,谢谢哈。

phillips 发表于 2005-10-17 17:47

我是由GBA開發入手學編程的,所以我的理解方式仍是由GBA而來,提供一些看法給大家參考一下...

這個應是由helloworld來的原碼改的,後來的pspsdk好像對該SCAN_LINE_SIZE的描述是 (是在sceDisplaySetFrameBuf這個函數的解釋中提到的...) 2的次方數。所以我覺得當時寫helloworld的作者的猜想是這個參數是掃描線的大小(長度),可是我猜它應是由FrameBuffer拷貝到Vram的傳輸帶寬。而不是指單行scanline的大小...(我在之前整理的PSP_LIB.xls已自作主張改成傳輸帶寬了...)

又vram位址是在0x4000000,加上了0x40000000的位址應是緩衝區,所以緩衝位址可以變...

在GBA中,垂直更新是發生在整個畫面畫完之後。因為畫面在更新中(由緩衝拷貝到vram),如果又下一段程式去寫緩衝,一定就會發生畫面有怪怪東西....所以才必須要「等一下」,等畫面更新完才可以進行下一段程式...

PSP好像是以線程(thread)的方式執行,所以按下HOME鍵時,其實是一道線程的作用。不過我是編程的新手,還沒搞清楚線程的好處....當然對HOME鍵相關的設定不是十分的了解了....


ARGB的A是透明度的意思啊 (第一次聽到...),但是有什麼作用呢? 也希望有高人可以來向大家上上課....

dr_watson 发表于 2005-10-17 19:58

>>china0008:

我最初用的是devkitPro, 安裝方法請參考:

[url=http://bbs.pspchina.net/read.php?tid=32263&fpage=1]简述安装devkitPSP开发环境, 完全免用Cygwin[/url]

後來我再三嘗試, 終於以cygwin+toolchain裝好pspsdk. 所以我現在開始用toolchain的pspsdk了. 但本文的例子, 並沒有應用任何特別的library. 所以用在devkitPSP 或 toolchain的pspsdk, 都應該可以.

>>phillips:

因為沒有sony官方的文件, 我對於vram的資料是來自:

[url=http://wiki.pspdev.org/psp:memory_map]psDevWiki[/url]

[quote]
To get uncached reads and writes, OR your pointers with 0×40000000 (for example, the location of the Ge VRAM then becomes 0×44000000). When writing to VRAM or feeding data to the GE (graphics chip) through display lists in main memory, this is VERY important. Not doing so will cause unpredictable behaviour and hanging because the graphics chip may miss your END and FINISH commands since they might still be sitting in the cache.
[/quote]

照原文所說, 直接用0x4000000, 才會有"緩衝"作用,  加了0x40000000, 反而是有"直接"效應.

至於關於應用sceDisplayWaitVblankStart(), 我也是參考psDevWiki的

[url=http://wiki.pspdev.org/psp:programming_faq]programming_faq[/url]

[quote]
Why does my PSP freeze at 'please wait...' when exited via the home menu? Why does making sceDisplayWaitVblankStart() or sprinkling around some unwanted file i/o fix it?
A:
Apparently a kernel thread needs to run here and the user game thread is not letting it. The aforementioned functions will let the kernel run; but also sceKernelDelayThread(0) has been known to work.
[/quote]

ARGB 內的 A 是指 Alpha, 所以我直稱之為透明度, 直接讀寫vram, 我試過是沒有什麼作用, 我想可能要運用3D的功能(OPENGL ES?), 才有用處.

另外要避免"畫面有怪怪東西...", 我看了幾個國外homebrew psp 遊戲原始碼, 得出的結論是光是sceDisplayWaitVblankStart是不夠的, 還要配合double buffering. 等我有時間應用到double buffering, 再和大家分享討論.

shally5 发表于 2005-10-17 20:21

[quote][b]下面是引用china0008于2005-10-17 14:32发表的:[/b]
大哥可以把您使用的  编译环境平台的  架设写一下吗?我到现在没有搞定环境啊。帮帮,谢谢哈。[/quote]

我用的就是DevKitPro很好安装
用start.bat设置环境
make进行编译程序
建议你就使用DevKitPro就行啦!
那里没有搞定你就问啊!

小朱仔仔 发表于 2005-10-17 21:54

對psp的開機軟件做修改的指令或函數是什麼?知道的人請告知,以免學寫編程時,不了解而誤用導致無法開機
而且分析從國外下載的源碼時,也可以分辨是不是惡作劇的程式

小朱仔仔 发表于 2005-10-17 21:56

讚賞樓主這種幾近詳解的編程教學,希望你持續下去

ssddn 发表于 2005-10-17 22:15

欢迎大家踊跃发布原创作品。

maxhoov 发表于 2005-10-17 22:20

>>>dr_watson
现在最缺少的就是中文PSP开发文献,相信你可以做的更好.

/maxhoov

dr_watson 发表于 2005-10-18 08:49

謝謝大家的鼓勵! 我不是什麼遊戲編程高手, 暫時的目標只是弄一個space invader或是撞磚的小遊戲, 只要有時間, 我會把整個過程記錄下來.

>>phillips:

再參考了一下 sceDisplayGetFrameBuf, 我會在以後的代碼里把SCAN_LINE_SIZE改為FRAME_BUFFER_WIDTH :)

小朱仔仔 发表于 2005-10-18 13:36

#define SCREEN_WIDTH           480
#define SCREEN_HEIGHT          272
#define SCAN_LINE_SIZE         512
1.請問main.c中  SCREEN_WIDTH,SCREEN_HEIGHT,SCAN_LINE_SIZE
  可以自行另外取其他的變數名稱嗎?例如:SCREEN_W  還是說這些都是
  pspkernel.h pspdisplay.h pspdebug.h 內所設定好的所以不能改它

2.螢幕只能夠設定為32bit一個像素嗎?如果設定成16bit,這個內容要如何改
  #define ARGB(a, r, g, b)     (a<<24|b<<16|g<<8|r)
  還有 (a<<24|b<<16|g<<8|r)這個不是註解吧,它的語法請說明一下

3.PSP_MODULE_INFO("Game 01", 0, 1, 1); 括弧中的數字跟PSP_MODULE_INFO是什麼意思
  如果不加這一個會怎麼樣

4.u32是什麼,做什麼用呢

5.您說一定要OR 0x40000000,可是這一行u32* pVRAM = (u32*)(0x04000000+0x40000000);
  我看不出來有做oR的運算,u32* pVRAM = (u32*)(0x04000000 OR 0x40000000) 這樣行嗎?

6.// Exit callback
int exit_callback(int arg1, int arg2, void *common)
{
      done = 1;
      return 0;
}
以上程式的用意是什麼/

7.還有// Callback thread 內程式的作用為何?

8.// Sets up the callback thread and returns its thread id 內程式的作用為何?

雖然我學過一些c編程,可是以上問題實在不明白,或許對你們來說很簡單,可是一定有許多人跟我一樣不會,請明白的網友
教學一下,感激不盡

phillips 发表于 2005-10-18 14:50

>>dr_watson

也很感謝你的這篇文章 (當然還有後續的也是),讓大家不僅僅有入門的空間,也可以有討論的主幹。
我本來也想共襄盛舉拿點東西出來的,可惜我的學習不像您那樣作筆記的工夫,要拿出來說明可能反而會搞得大家不知所云...
也謝謝你提供的DEVKITPSP的安裝方法,我現在由PSPDEV改成這個了。因為它支援的指令好像比較多...

>>小朱仔仔
1. 是可以自定的,只要餵給相關的函數時不要忘記自己自定成什麼名稱就可以了...
2. PSP中"好像"有四種顯示模式,至少在餵給sceDisplaySetFrameBuf這個函數時是有四個選擇。
  函數原型是:
  sceDisplaySetFrameBuf(void *topaddr, int bufferwidth, int pixelformat, int sync)
  其中的Pixelformat就是模式設定。0 : RGB565, 1 : RGBA5551, 2 : RGBA4444, 3 : RGBA8888。由上面的數字可知模式0~2三種模式都是16bits一個像素,只有3是32bits。
3. 最近在看VB.Net它可能和.net的版本管理一樣,或是為了相容於.net的要求的版本管理函數。不是非必要的東西吧...
4. u32是unsigned int (好像吧?)的意思,由於32表4個byte(4*8),用u32來書寫比較不會發生溢位之類的邏輯錯誤...
5.

ssddn 发表于 2005-10-18 15:33

5. u32* pVRAM = (u32*)(0x04000000 | 0x40000000)
6,7. 当你按下Home键退出时系统将调用exit_callback,它将done置为1,这样使main函数中的while循环退出。
8. 创建一个线程。

小朱仔仔 发表于 2005-10-18 16:34

我想請問的是
1.#define 後面接的東西是不是完全是自己定的變量名稱,還是不一定也有可能是既有的函數或關鍵字
2.所以說u32* pVRAM = (u32*)(0x04000000+0x40000000)跟u32* pVRAM = (u32*)  
  (0x04000000  | 0x40000000) 的意思是一樣的嗎?  一個是+   一個是 |
3. 所以說u32* pVRAM就是把 pVRAM  定義為 unsigned int 型態的變量名稱對嗎?
4.也就是說如果寫其他程式對於Home的處理就是把// Exit callback跟// Callback thread
的內容照抄於程式里對嗎?

dr_watson 发表于 2005-10-18 22:06

再次謝謝大家的支持, 跟這麼多志同道合的朋友一起學習討論, 真是興奮!

>>小朱仔仔:
1. psp的螢幕是480x272, 而大家都是用這個設定, 至於能否用別的, 我到現在也沒有看到其他例子.
2. OR 和 + 並不是所有時候的結果都是一樣的.如 (3+1==4) 而 (3 | 1 == 3), 要轉為二進位, 才比較容易看得出來. 把兩個數 OR, 其中一個數的BIT為1, 答案的BIT即為1, 當兩個同位置的BIT同為0, 答案的BIT才為0:
[code]
      001010
OR  100110
--------------
=    101110
[/code]
3. 對.
4. 是. 這只是從HELLO WORLD 改出來的, 這部份基本每個程式都可以照抄.

lu_pp 发表于 2005-10-19 09:00

支持楼主
有没有BLOG?
我要订阅

dr_watson 发表于 2005-10-19 16:08

>>> lu_pp

有是有blog, 但我很少更新, 也沒有什麼內容.

我主要會以PSPChina的軟件開發版為基地, 和大家分享我認為有用或好玩的東西, 一來我覺得和大家分享交流很開心, 二來也是要報答ssddn大大把我第一篇發稿置頂之恩 ;)

ssddn 发表于 2005-10-19 21:56

to dr_watson

很高兴你能在这里让大家分享你的成果。
也十分期待着你的后续作品。
当你的这个系列完成后我仍然会给置顶并加精的。

dr_watson 发表于 2005-10-20 08:05

謝謝ssddn大大的鼓勵!

我再次考慮了一下maxhoov大大的建議, 決定把相關內容在放在一起, 方便大家找, 也不想把論壇目錄弄得太亂;)

我在論壇發貼是新手, 有不當之處, 還請多多包涵指點:)

页: [1] 2 3 4 5 6 7 8 9
Google
 

Powered by Discuz! Archiver 7.2  © 2001-2009 Comsenz Inc.