最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

08-對part5-breakout的搬運和翻譯

2023-08-13 22:14 作者:Xdz-2333  | 我要投稿

所有圖均為原文所有,并非我自己添加

非黑色字體均為我自己添加,

原文放在文章末尾

對Breakout的介紹

????????Breakout是一款經(jīng)典的街機游戲,它有一個槳(或者說球拍),你可以在屏幕底部橫向上移動它.屏幕的頂部是一行行磚頭.一個球在屏幕上跳來跳去,在球拍和磚頭之間反彈,當磚塊被擊中時就會被"撞碎".游戲目標是用球撞碎所有的磚頭.如果你不及時接住球,那么它就會從屏幕下方出去,那你就死了一條命.一般情況下你死三次游戲就結(jié)束了.

????????對游戲引擎感興趣的同學可以去聽games104,b站上就有.

????????神奇的是,我們有所有用來構(gòu)建我們的版本的?Steve Wozniak的經(jīng)典作品的組件.


作者給出的Steve Wozniak的經(jīng)典游戲的圖片

對我們繪圖代碼的調(diào)整

????????如果你也運行在1080p之下,你就會意識到8*8的字體對于屏幕來講實在是太小了.?讓我們修改drawChar,使其帶上zoom參數(shù),這樣我們的游戲玩家就不用讓鼻子碰到電視上了.

void drawChar(unsigned char ch, int x, int y, unsigned char attr, int zoom)

{

? ? unsigned char *glyph = (unsigned char *)&font + (ch < FONT_NUMGLYPHS ? ch : 0) * FONT_BPG;


? ? for (int i=1;i<=(FONT_HEIGHT*zoom);i++) {

? ? ? ? for (int j=0;j<(FONT_WIDTH*zoom);j++) {

? ? ? ? ? ? unsigned char mask = 1 << (j/zoom);

? ? ? ? ? ? unsigned char col = (*glyph & mask) ? attr & 0x0f : (attr & 0xf0) >> 4;


? ? ? ? ? ? drawPixel(x+j, y+i, col);

? ? ? ? }

? ? ? ? glyph += (i%zoom) ? 0 : FONT_BPL;

? ? }

}

????????我們只是通過比例因子zoom來改變我們的循環(huán)從而改變我們的列高和行寬.比如說,當zoom設(shè)置為2的時候,我們的8*8位圖實際上被渲染為16*16的位圖.可視化的輸出對我們的理解循環(huán)中所需要的邏輯有所幫助:

0 0 0 0 1 1 0 0 -> 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0?

? ? ? ? ? ? ? ? ? ? ? ? ? ??0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0?

0 0 0 1 1 1 1 0 -> 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0?

? ? ? ? ? ? ? ? ? ? ? ? ? ??0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0?

0 0 1 1 0 0 1 1 -> 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1?

? ? ? ? ? ? ? ? ? ? ? ? ? ??0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1?

0 0 1 1 0 0 1 1 ->?0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1?

? ? ? ? ? ? ? ? ? ??????????0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1?

0 0 1 1 1 1 1 1 -> 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1

? ? ? ? ? ? ? ? ? ????????? 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1

0 0 1 1 0 0 1 1 -> 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1??

? ? ? ? ? ? ? ? ? ????????? 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1??

0 0 1 1 0 0 1 1 -> 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1??

? ? ? ? ? ? ? ? ? ????????? 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1??

0 0 0 0 0 0 0 0 -> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0?

? ? ? ? ? ? ? ? ? ????????? 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0?

????????很明顯我們想要重復(fù)每個位zoom次來填滿更寬的行(橫向縮放).并且我們想要重復(fù)整行zoom次來填滿更高的列(縱向縮放).

????????為了達成前一項,我們只需要在每次縮放而不是每次都左移一位掩碼.我們利用整數(shù)四舍五入:

zoom=1? ? ? ? ? ? ? ? ? ? ? ? ???zoom=2

j=0 : (j / zoom) = 0? ? ? ? ? ?j=0 : (j / zoom) = 0

j=1 : (j / zoom) = 1? ? ? ? ???j=1 : (j / zoom) = 0

j=2 : (j / zoom) = 2? ? ? ? ?? j=2 : (j / zoom) = 1

j=3 : (j / zoom) = 3? ? ? ? ? ?j=3 : (j / zoom) = 1

j=4 : (j / zoom) = 4? ? ? ? ?? j=4 : (j / zoom) = 2

j=5 : (j / zoom) = 5? ? ? ? ? ?j=5 : (j / zoom) = 2

j=6 : (j / zoom) = 6? ? ? ?? ? j=6 : (j / zoom) = 3

j=7 : (j / zoom) = 7? ? ? ? ? ?j=7 : (j / zoom) = 3

...? ? ? ? ? ? ? ? ? ? ? ? ??????????? ? ...

????????這樣就解決了水平縮放問題.用類似的方法來解決縱向放大問題.我們使用模運算來完成glyph 指針的迭代遞增(我認為這是"先除后余"),而不是在每次迭代時以每行的字節(jié)數(shù)遞增指針.如果i被zoom除后沒有余數(shù),我們遞增我們的 glyph 指針,否則我們就把它留在原來的位置.

zoom=2

i=1 : (i % zoom) = 1

i=2 : (i % zoom) = 0 -> advance the pointer?

i=3 : (i % zoom) = 1

i=4 : (i % zoom) = 0 -> advance the pointer

i=5 : (i % zoom) = 1

i=6 : (i % zoom) = 0 -> advance the pointer

i=7 : (i % zoom) = 1

i=8 : (i % zoom) = 0 -> advance the pointer

...

????????你可能明白了為什么我們的外層循環(huán)從1開始而不是0?

????????如果你想,你可以現(xiàn)在就在你part5-framebuffer中的?fb.c中做出這些修改,并在kernel.c中執(zhí)行它們來檢查是否正確.別忘了更新 fb.h 中的函數(shù)定義來包含zoom參數(shù).

????????現(xiàn)在修改drawString并且加上一個zoom參數(shù)并且傳遞它是很平凡的了.所以我不會在這里記錄這些變化.

目標跟蹤

????????現(xiàn)在我們可以畫文本,(比如分數(shù)或者剩下幾條命),矩形(槳或者磚)和圓形(球),我們可以在屏幕上復(fù)現(xiàn)Breakout這個游戲了.檢查我們的新 kernel.c中的 initBall(), initPaddle(), initBricks()? 和? drawScoreboard(score, lives) .

????????除了圖形學代碼,你可以看見我們在全局的 object 數(shù)組中記錄了我們創(chuàng)造的每一個游戲中的物品.

  • 它的 (x,y) 坐標

  • 它的高和寬

  • 物品的類型(球,牌子或者磚頭)

  • 它是否還"活著"

????????我們還存儲了一個指向ball和paddle對象的全局指針,這樣就很容易跟蹤到它們

????????因為在游戲中我們需要撞碎我們的磚頭,我創(chuàng)建了 removeObject(object),它只是把我們傳進去的objec涂黑,然后把alive這個參數(shù)改為0來表示它不再參與游戲.

????????為了知道你的球?qū)⒁鲎驳酱u頭(或者實際上是槳),我們需要碰撞檢測(沒檢測碰撞一般會發(fā)生穿模).我們只需要對仍然存活(alive)的對象進行搜索,然后返回第一個坐標重疊的對象.如果沒有物品被發(fā)現(xiàn),我們就返回0.detectCollision(object, xoff, yoff) 實現(xiàn)了這些.注意 xoff 和 yoff 可能為負,因為球會往任何方向走.

鍵盤輸入

????????我們將要用串口來接收輸入,就像我們在 part4-miniuart里面做的那樣.

????????getUart()只是檢測是否有任何鍵被按下,如果有,它就返回字符,不然返回0.我們不希望這個函數(shù)一直等待一個鍵被按下,因為無論如何游戲都要繼續(xù).

讓游戲動起來

????????讓我們看看新的main()例程,它控制著游戲.大多數(shù)初始化代碼對你來講應(yīng)該很熟悉,游戲結(jié)束時的Game over和 Well done 也應(yīng)該很清楚.因此我將聚焦在內(nèi)部循環(huán)的函數(shù)上.

????????首先,我們檢測我們是否移動了paddle.按 "h"左移,"l"右移.我一般使用方向鍵,但是它們在UART上有些難以捕捉,所以讓我們先保持簡單(上下左右的方向鍵一般會轉(zhuǎn)換成好幾個字符,因此UART接收時需要一個狀態(tài)機來進行判斷)

????????現(xiàn)在我們實現(xiàn)了另一個函數(shù) moveObject(object, xoff, yoff)來移動這些.這個從我在 fb.c中實現(xiàn)的另一個例程的調(diào)用開始,這個例程將矩形的位圖從一個地方移動到另一個,只留下背景顏色.這是一個草率的實現(xiàn),但現(xiàn)在可以了.我們只是更新object的(x,y)坐標,然后返回.

????????有了paddle的邏輯,我們現(xiàn)在來處理球.我們設(shè)置了一些初始化的速度,但是,在我們移動球之前,我們需要碰撞檢測.當我們要撞到磚頭的時候,我們:

  • 使用removeObject移除磚頭

  • 反轉(zhuǎn)我們的法向速度

  • 增加分數(shù)

  • 重新繪制得分板

????????當我們要碰撞到槳的時候,我們:

  • 反轉(zhuǎn)我們的法向速度

  • 在水平方向上做出必要的改變(如果我們撞到了邊緣)

????????我們可以在稍微延遲后移動我們的球(所以游戲速度不會很快).延遲代碼在fb.c中實現(xiàn),使用了arm的板上計時器.

????????最后,我們必須確保我們的球在游戲區(qū)域內(nèi)活動.如果我們需要,我們可以反彈球,但是如果它從屏幕底下逃走了,我們就損失一條命,并且重置槳和球.這實際上十分簡單,因為我們可以移除它們?nèi)缓髣?chuàng)造新的!

結(jié)論

????????我希望你編寫你自己的第一個游戲比你自己想的簡單的多 -- 在裸機上也是一樣.它十分的簡單,確實是的,但是一旦你完成它并且在樹莓派4上運行,我打賭你會有些沉迷于它.我知道我是的!

????????好的,你現(xiàn)在完成了你的第一個游戲.

PS:如果你在使用arm gcc中遇到問題(即繪制第一塊磚后意外崩潰),你可能需要試試把編譯器優(yōu)化從 Makfile中的 -O2 調(diào)整為 -O1.有些人報告了這個問題(https://github.com/isometimes/rpi4-osdev/issues/17),并且我能夠復(fù)現(xiàn).

作者實現(xiàn)的游戲效果圖

Writing a "bare metal" operating system for Raspberry Pi 4 (Part 6)

Introducing Breakout

[Breakout](https://www.gameinformer.com/b/features/archive/2015/10/09/how-steve-wozniak-s-breakout-defined-apple-s-future.aspx) is a classic arcade game that has a paddle (or bat), which you move along a horizontal axis at the bottom of the screen. At the top of the screen are rows of bricks. A ball bounces around the screen, rebounding off the bat and off the bricks, which are "knocked out" when directly hit. The goal is to knock out all of the bricks with the ball. If you don't get to the ball in time, and it escapes the bottom of the screen, you lose a life. You typically have 3 lives before Game Over.


Amazingly, we have all the components we need to build our version of [Steve Wozniak](https://en.wikipedia.org/wiki/Steve_Wozniak)'s classic.


![Steve Wozniak's Breakout game](images/6-breakout-wozniak.jpg)


A tweak to our drawing code

If you're also running in 1080p, then you'll note that our 8x8 font is rather small on the screen. Let's tweak `drawChar` to take a `zoom` parameter so our game players don't need to have their noses against the TV!


```c

void drawChar(unsigned char ch, int x, int y, unsigned char attr, int zoom)

{

? ? unsigned char *glyph = (unsigned char *)&font + (ch < FONT_NUMGLYPHS ? ch : 0) * FONT_BPG;


? ? for (int i=1;i<=(FONT_HEIGHT*zoom);i++) {

? ? ? ? for (int j=0;j<(FONT_WIDTH*zoom);j++) {

? ? ? ? ? ? unsigned char mask = 1 << (j/zoom);

? ? ? ? ? ? unsigned char col = (*glyph & mask) ? attr & 0x0f : (attr & 0xf0) >> 4;


? ? ? ? ? ? drawPixel(x+j, y+i, col);

? ? ? ? }

? ? ? ? glyph += (i%zoom) ? 0 : FONT_BPL;

? ? }

}

```


We're just changing our loops to increase the column height and row width by our scale factor `zoom`. With `zoom` set to 2, for example, our 8x8 bitmap will actually be rendered as a 16x16 bitmap. To understand the logic required within the loop, it helps me to visualise the desired output:


```c

0 0 0 0 1 1 0 0 -> 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0?

? ? ? ? ? ? ? ? ? ?0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0?

0 0 0 1 1 1 1 0 -> 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0?

? ? ? ? ? ? ? ? ? ?0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0?

0 0 1 1 0 0 1 1 -> 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1?

? ? ? ? ? ? ? ? ? ?0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1?

0 0 1 1 0 0 1 1 -> 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1?

? ? ? ? ? ? ? ? ? ?0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1?

0 0 1 1 1 1 1 1 -> 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1

? ? ? ? ? ? ? ? ? ?0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1

0 0 1 1 0 0 1 1 -> 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1??

? ? ? ? ? ? ? ? ? ?0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1??

0 0 1 1 0 0 1 1 -> 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1??

? ? ? ? ? ? ? ? ? ?0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1??

0 0 0 0 0 0 0 0 -> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0?

? ? ? ? ? ? ? ? ? ?0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0?

```


It's clear that we want to repeat each bit `zoom` times to fill the wider row (horizontal zoom). And we then want to repeat the entire line `zoom` times to fill the taller column (vertical zoom).


To achieve the former, we simply advance our bitmask one in every `zoom` times instead of every time. We make use of integer rounding thus:


```c

zoom=1? ? ? ? ? ? ? ? ? ? ?zoom=2

j=0 : (j / zoom) = 0? ? ? ?j=0 : (j / zoom) = 0

j=1 : (j / zoom) = 1? ? ? ?j=1 : (j / zoom) = 0

j=2 : (j / zoom) = 2? ? ? ?j=2 : (j / zoom) = 1

j=3 : (j / zoom) = 3? ? ? ?j=3 : (j / zoom) = 1

j=4 : (j / zoom) = 4? ? ? ?j=4 : (j / zoom) = 2

j=5 : (j / zoom) = 5? ? ? ?j=5 : (j / zoom) = 2

j=6 : (j / zoom) = 6? ? ? ?j=6 : (j / zoom) = 3

j=7 : (j / zoom) = 7? ? ? ?j=7 : (j / zoom) = 3

...? ? ? ? ? ? ? ? ? ? ? ? ...

```


So, that sorts out our horizontal zoom. To achieve the vertical zoom, it's a similar solution. Instead of advancing our glyph pointer by the number of bytes per line on each iteration, we do it one in every `zoom` iterations by making use of the **modulo** operator (I think of this as "remainder after division"). If `i` divided by `zoom` has no remainder then we advance our glyph pointer, otherwise we leave it where it is!


```c

zoom=2

i=1 : (i % zoom) = 1

i=2 : (i % zoom) = 0 -> advance the pointer?

i=3 : (i % zoom) = 1

i=4 : (i % zoom) = 0 -> advance the pointer

i=5 : (i % zoom) = 1

i=6 : (i % zoom) = 0 -> advance the pointer

i=7 : (i % zoom) = 1

i=8 : (i % zoom) = 0 -> advance the pointer

...

```


Perhaps you can see why we changed our outer loop to count from 1 rather than 0?


If you want, you can now make these changes to _fb.c_ in your part5-framebuffer code and exercise them properly in _kernel.c_ to check that they work. Don't forget to update the function definition in _fb.h_ to include the `zoom` parameter too.


It is also now trivial to modify `drawString` to take a `zoom` parameter and pass it through, so I won't document the changes here.


Object tracking

As we can now draw text (e.g. a score/lives counter), rectangles (paddle & bricks) and circles (ball), we can recreate the Breakout game screen. Check out `initBall()`, `initPaddle()`, `initBricks()` and `drawScoreboard(score, lives)` in our new _kernel.c_.


In addition to the graphics code, you'll see that we're keeping a record of each game object we create in the global `objects` array:


?* its (x, y) coordinates

?* its width & height

?* what type of object it is (ball, paddle or brick)

?* whether it's "alive"


We also store a global pointer to the ball and paddle object, so they're easy to track down!


As we'll need to knock out our bricks during gameplay, we create `removeObject(object)`, which simply draws a filled black rectangle over the object we pass, and sets its `alive` parameter to 0 to signal that it's now out of play.


To know that our ball is about to hit a brick (or indeed the paddle), we'll need to detect **collisions**. We simply conduct a search of the alive objects and return the first object we find whose coordinates overlap. If no object is found, we return 0. `detectCollision(object, xoff, yoff)` implements this. Note that `xoff` and `yoff` can be negative since the ball could be travelling in any direction.


Keyboard input

--------------


We'll be using the UART to take input, just like we did in part4-miniuart.


`getUart()` simply checks if a key has been pressed and, if so, it returns the character, otherwise 0. We don't want this function to wait for a key, because gameplay needs to continue regardless.


Animating the gameplay

Let's look at the new `main()` routine which controls the gameplay. Most of the initialisation code should be familiar to you, and the endgame of "Game over" or "Well done!" should also be clear. I'll therefore focus on the function of the inner loop.


First, we check if we need to move the paddle. We'll move it left if we get a keypress of "h", and right if we get a keypress of "l". I would use the arrow keys normally, but they're a little harder to capture over the UART so we'll keep it simple for now.


We now implement another function `moveObject(object, xoff, yoff)` to make these moves. This starts by calling another routine I've implemented in _fb.c_ to move a rectangular bitmap from one screen location to another, leaving only the background colour behind. It's a sloppy implementation, but it will do for now. We then just update the object's (x, y) coordinates and return.


With our paddle logic in place, let's deal with the ball. We've set some initial velocities but, before we actually move the ball, we need to check for collisions. If we're about to hit a brick, we:


?* remove that brick using `removeObject`

?* reverse our vertical direction

?* increment the score?

?* redraw the scoreboard


If we're about to hit the paddle, we:


?* reverse our vertical direction?

?* make any necessary changes to our horizontal direction (in case we hit the side)


We can then move our ball after a slight delay (so gameplay isn't too fast). The delay code is implemented in _fb.c_ and uses the on-board timer on the ARM.


Finally, we just need to make sure the ball is in the game arena. We bounce it off the sides if we need to but, if it escapes the bottom of the screen then we lose a life and reset both the paddle & ball. This is easy enough since we can just remove them and create new ones!


Conclusion

I hope writing your first game was easier than you thought it might be - and on bare metal too. It's pretty simple, granted, but once you've built it and got it going on your RPi4, I bet you'll be just a little bit addicted. I know I am!


_Well done, you've just written your first game!_


PS: if you have issues using the Arm gcc compiler (namely an unexpected crash after painting the first brick), you might to try setting the compiler optimisation level to `-O1` instead of `-O2` in the _Makefile_. Some folks have [reported issues](https://github.com/isometimes/rpi4-osdev/issues/17), and I am able to reproduce these.


08-對part5-breakout的搬運和翻譯的評論 (共 條)

分享到微博請遵守國家法律
苍南县| 土默特左旗| 永善县| 郧西县| 沙洋县| 宣化县| 固阳县| 乌恰县| 弋阳县| 长宁区| 象州县| 台前县| 安泽县| 尉犁县| 项城市| 来宾市| 墨江| 天津市| 子长县| 长白| 怀来县| 晋宁县| 精河县| 扎鲁特旗| 兴山县| 鹿邑县| 琼海市| 平塘县| 柞水县| 五家渠市| 长阳| 曲麻莱县| 潞西市| 武威市| 梁平县| 乐山市| 乌鲁木齐县| 保靖县| 新昌县| 大石桥市| 安阳市|