欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

golang小游戲開發(fā)實戰(zhàn)之飛翔的小鳥

 更新時間:2024年03月02日 12:00:04   作者:壹個人的火鍋  
這篇文章主要給大家介紹了關于golang小游戲開發(fā)實戰(zhàn)之飛翔的小鳥的相關資料,,本文可以帶你你從零開始,一步一步的開發(fā)出這款小游戲,文中通過代碼介紹的非常詳細,需要的朋友可以參考下

游戲開發(fā)總體思路

首先要選取一個合適的圖形化界面進行開發(fā)。該項目選取的是 ebiten 一個用于創(chuàng)建2D游戲和圖形應用程序的游戲引擎,提供了一些簡單的GUI功能。

其次明確游戲設計思路。飛翔的小鳥共分為三個場景。

第一個場景就是游戲開始前的準備階段,讓玩家點擊屏幕確定游戲開始。

第二個場景就是讓游戲正式開始,玩家可以操控小鳥進行游戲。

第三個場景就是游戲結束,顯示分數(shù)的階段。

一、先讓窗口和背景繪制出來

先簡單的讓窗口顯示出來,通過ebiten.RunGame(&game)啟動游戲引擎,會開始初始化游戲并且開始循環(huán)執(zhí)行游戲的更新和渲染邏輯。

游戲運行后 會自動調用Update函數(shù)進行屏幕刷新,然后再進行游戲繪制。

一般情況下Update函數(shù)是寫游戲邏輯的,Draw函數(shù)進行游戲回話。

func main() {
	// 設置窗口大小是
	ebiten.SetWindowSize(880, 520)
	// 設置窗口頭部,顯示 飛翔的小鳥
	ebiten.SetWindowTitle("飛翔的小鳥")

	// 運行游戲
	err := ebiten.RunGame(&game);
	if err != nil {
		log.Fatal(err)
	}
}

/*
	Layout()函數(shù)的返回值表示顯示窗口里面邏輯上屏幕的大小
	官網(wǎng)上說參數(shù)outsideWidth和outsideHeight是顯示在桌面的窗口大小
	這里是固定大小
*/
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
	return 880, 520
}

func (g *Game) Update(screen *ebiten.Image) error {
	g.DrawBegin(screen)
	return nil
}

func (g *Game) DrawBegin(screen *ebiten.Image) {
	DrawBackGround(screen)
}
}

這里是進行游戲背景的渲染的,screen.Fill 是將整個畫面都填充完一種顏色。對于地板來說,進行一張圖片通過for循環(huán)來進行反復繪制。

大概就是這個樣子,for循環(huán),每次只用讓圖片的x坐標進行增加就可以了,這樣就把背景給渲染出來了。

func DrawBackGround(screen *ebiten.Image){
	//  背景顏色
	screen.Fill(color.RGBA{78, 192, 202,255})
	//  繪制地板
	f1, err := os.Open("imgs/ground.png")
	if err != nil {
		log.Fatal(err)
	}
	img1, err := png.Decode(f1)
	if err != nil {
		log.Fatal(err)
	}
	filter1 := ebiten.FilterNearest
	// 把Image文件轉成ebiten.Image文件,用于展示
	eImg1, _ := ebiten.NewImageFromImage(img1,filter1)

	var groundX int = 0;
	var groundY int = 437;
	for i :=1;i<5;i++ {
		op1 := &ebiten.DrawImageOptions{}
		op1.GeoM.Translate(float64(groundX), float64(groundY))   // 圖像坐標
		groundX+=250
		// 在屏幕上展示出圖片
		screen.DrawImage(eImg1, op1)

	}

這樣一個帶有背景圖片的窗口就繪制完成了。

二、游戲準備階段

該階段制作相對容易,因為此時只用基于上面那個畫面,添加一個鼠標點擊事件,讓玩家點擊后即可進入到下一個場景。

先傳入兩個照片,一個是顯示游戲名,一個是點擊進入到游戲的圖片。

然后對第二個圖片添加一個鼠標點擊事件,讓玩家點擊圖片進入到游戲中!

ebiten.IsMouseButtonPressed是鼠標點擊事件,關鍵要確定好鼠標點擊的范圍!

func (g *Game) DrawReady(screen *ebiten.Image)  {
	g.DrawBegin(screen)

	imageObject1, _, _ := ebitenutil.NewImageFromFile("imgs/title.png",ebiten.FilterDefault)
	imageObject2, _, _ := ebitenutil.NewImageFromFile("imgs/start.png",ebiten.FilterDefault)

	gameReady := &GameReady{
		imgReady1: imageObject1,
		img1X: 370,
		img1Y: 140,
		imgReady2: imageObject2,
		img2X: 333,
		img2Y: 200,
	}

	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(float64(gameReady.img1X),float64(gameReady.img1Y))
	screen.DrawImage(gameReady.imgReady1,op)

	op.GeoM.Reset()
	op.GeoM.Translate(float64(gameReady.img2X),float64(gameReady.img2Y))
	screen.DrawImage(gameReady.imgReady2,op)

	if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
		x,y := ebiten.CursorPosition()
		rect := image.Rect(gameReady.img2X,gameReady.img2Y,gameReady.img2X+200,gameReady.img2Y+30)
		if (x >=rect.Min.X && x<=rect.Max.X && y>=rect.Min.Y && y<= rect.Max.Y ){
			gameState = 1
		}
	}

}

三、游戲開始階段

這個場景有兩個重要的對象需要進行處理。小鳥和障礙物。

小鳥:需要進行繪制,并且給小鳥添加邏輯。每次跳躍修改坐標,并且碰到天空和地面進行over處理。

小鳥的制作

繪制

首先進行小鳥的繪制。繪制其實只用讀取小鳥的圖片,然后給小鳥設置一個初始坐標,然后展示在屏幕中。

f, err := os.Open(g.Bird)
	if err != nil {
		log.Fatal(err)
	}
	img, err := png.Decode(f)
	if err != nil {
		log.Fatal(err)
	}
	filter := ebiten.FilterNearest

	// 把Image文件轉成ebiten.Image文件,用于展示
	eImg, _ := ebiten.NewImageFromImage(img,filter)

	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(g.LocationX,g.LocationY)

	// 在屏幕上展示出圖片
	screen.DrawImage(eImg, op)

邏輯處理

小鳥因為只用上下移動,它的邏輯處理起來還是比較簡單的,只用通過按鍵響應監(jiān)聽空格,每按一下空格,小鳥的坐標就減少,松開空格后坐標就想加。(這是因為 這個圖像左上角的坐標是0 0,向右和向下分別增加x和y的坐標值)。

同時也要對 小鳥狀態(tài)進行判斷,當觸碰到天空和地面時,進行over處理,然后每次按

//  按空格 跳躍
	if ebiten.IsKeyPressed(ebiten.KeySpace) && g.BirdState != 0 && !isSpace && gameState == 1 &&!isstop {
		isSpace = true
		locationTemp = -5
		g.Bird = "imgs/up.png"
	}

	//  松開空格
	if !ebiten.IsKeyPressed(ebiten.KeySpace) && gameState == 1{
		locationTemp = 2
		g.Bird = "imgs/down.png"
		isSpace = false
	}

	//  判斷是否暫停,然后判斷 小鳥此時 能否移動
	if !isstop { // 暫停
		g.LocationY = g.LocationY + float64(locationTemp)
	}

	// 碰到下邊界  和  上邊界
	if (g.LocationY >= 409 || g.LocationY <= -4 ){
		g.Bird = "imgs/die.png"
		g.BirdState = 0
	}
	//  死亡不讓超出下邊界
	if (g.LocationY >= 479){
		g.LocationY = 479;
	}

	//  小鳥死亡
	if (g.BirdState == 0){
		g.Bird = "imgs/die.png"
		g.LocationY +=3
		locationTemp = 3
	}

障礙物的制作

障礙物的制作也分為兩個方面,一個就是障礙物的繪制和邏輯處理。

障礙物的邏輯處理還是相對簡單的,因為障礙物只需要一直向左邊移動,所以只用改變障礙物的x坐標就可以了。

復雜的是障礙物的繪制,因為障礙物有三種形態(tài),每種形態(tài)的繪制還是隨機的,不能重復。

繪制

考慮到障礙物的長度不能一樣,所以就想到了通過 隨機數(shù)來確定障礙物的長度。并且也需要通過隨機數(shù)來確定障礙物的哪一種形態(tài)的。

可以先獲取一個隨機數(shù),來確定障礙物的長度。然后通過for循環(huán)來繪制障礙物。(通過for循環(huán)是因為障礙物 和 地方一樣 它們的圖片都是非常短的一截,確定長度后通過循環(huán)進行繪制)。

下面是三種障礙物的繪制方法,繪制后將其存入到切片中。

var obstacles []*NewBarrier // 切片存數(shù)組
// 下方障礙物
func (o *NewBarrier) DrawTop(screen *ebiten.Image) {
	for i := 1 ;i<o.TopLength;i++{
		op := &ebiten.DrawImageOptions{}
		op.GeoM.Translate(float64(o.X),float64(o.TopY+(i-1)*20))
		screen.DrawImage(o.Image,op)
	}
	op1 := &ebiten.DrawImageOptions{}
	op1.GeoM.Translate(float64(o.X-2),float64(o.TopLength*20-20))
	screen.DrawImage(o.BottomImage,op1)
}

// 上方障礙物
func (o *NewBarrier) DrawBottom(screen *ebiten.Image) {
	for i := 1 ;i<19-o.TopLength;i++{
		op := &ebiten.DrawImageOptions{}
		op.GeoM.Translate(float64(o.X),float64(o.BottomY-(i-1)*20))
		screen.DrawImage(o.Image,op)
	}
	op1 := &ebiten.DrawImageOptions{}
	op1.GeoM.Translate(float64(o.X-2),float64(500-(19-o.TopLength)*20+20))
	screen.DrawImage(o.UpImage,op1)
}

// 中間障礙物
func (o *NewBarrier) DrawMid(screen *ebiten.Image) {
	//fmt.Println(o.HoverUPY,o.TopLength,o.HoverBottomY)
	for i := 1 ;i<o.TopLength;i++{
		op := &ebiten.DrawImageOptions{}
		op.GeoM.Translate(float64(o.X),float64(o.HoverUPY+i*20))
		screen.DrawImage(o.Image,op)
	}
	op1 := &ebiten.DrawImageOptions{}
	op1.GeoM.Translate(float64(o.X-2),float64(o.HoverBottomY))
	screen.DrawImage(o.BottomImage,op1)
	op2 := &ebiten.DrawImageOptions{}
	op2.GeoM.Translate(float64(o.X-2),float64(o.HoverUPY))
	screen.DrawImage(o.UpImage,op2)
}

//  繪制障礙物
func (g *Game) DrawBarrier(screen *ebiten.Image){
	if (gameState != 0){
		for _, o := range obstacles {
			if (o.BarrierStates ==0 || o.BarrierStates==1){
				o.DrawTop(screen)
				o.DrawBottom(screen)
			}else{
				o.DrawMid(screen)
			}
			if (o.X<200){
				if (!o.isScore){
					o.isScore = true
					Score++
				}
			}
		}
	}
}

邏輯處理

障礙物的邏輯處理其實非常簡單,只用遍歷切片,讓每個障礙物的坐標一直減小就可以了。

// 讓障礙物移動起來
func obojsaasdf(obs []*NewBarrier){
	for i:=0;i< len(obs);i++ {
		obs[i].X --
	}
}

障礙物對象池

因為障礙物的數(shù)量考慮到非常多,并且一直有障礙物會移出屏幕,移出屏幕之后便看不見了,如果不對其進行妥善處理,我感覺會對內存有很大的消耗。所以就引入了類似于java對象池一樣的池子。將超出屏幕的障礙物 修改其部分屬性 然后重新添加到切片末尾,這樣便只用初始化幾個障礙物,然后這些障礙物反復利用,便可大大節(jié)省內存消耗。

func deal()  {
	if len(obstacles) > 0 {
		rand.Seed(time.Now().UnixNano())
		for i := 0; i < len(obstacles); i++{
			if obstacles[i].X < -50 {
				obstacles[i].TopLength = util.Random(15,2)
				// 移除超出屏幕的障礙物
				del := obstacles[0]
				del.X = obstacles[len(obstacles)-1].X+180
				ran := util.Random(15,2)
				ran1 := util.Random(180,80)
				del.TopLength = ran
				del.HoverUPY = ran1
				del.HoverBottomY = ran1 + 20*ran
				del.BarrierStates = util.Random(3,0)
				del.isScore = false
				obstacles = append(obstacles[:i], obstacles[i+1:]...)
				// 添加新的障礙物到切片末尾
				obstacles = append(obstacles, del)
			}
		}
	}
}

碰撞檢測處理

當小鳥和障礙物都繪制完了,就需要考慮他們之間的碰撞檢測了!

碰撞檢測采用的是獲取小鳥四個角的坐標和障礙物的坐標,讓他們的坐標沒有交集。

綠色的是障礙物,藍色的是小鳥。有一點抽象了。。。圖中這幾種情況是不會發(fā)生碰撞的情況。

所以,對于上下型障礙物來說,當小鳥的右邊 < 障礙物的左邊 或者 小鳥的左邊 > 障礙物的右邊 或者 小鳥的上邊 > 障礙物的下面 并且小鳥的下面 < 障礙物的上面

對于懸浮障礙物來說,x 坐標考慮和上面一樣,對于 y 坐標。小鳥的下面 < 障礙物的上面 或者 小鳥的上面 > 障礙物的下面

// 碰撞檢測
func IsColliding(birdX, birdY float64, birdWidth, birdHeight float64, barrierX, barrierTopY, barrierBottomY float64, barrierWidth int,HoverUp float64,HoverBottom float64) bool {
	birdLeft := birdX
	birdTop := birdY
	birdRight := birdLeft + birdWidth
	birdBottom := birdTop + birdHeight

	barrierLeft := barrierX
	barrierTop := barrierTopY
	barrierRight := barrierLeft + float64(barrierWidth)
	barrierBottom := barrierBottomY

	if birdRight < barrierLeft || birdLeft > barrierRight || ((birdBottom < barrierBottom) && (birdTop > barrierTop)) || ((birdBottom < HoverUp) || (birdTop > HoverBottom+20)) {
		// 沒有碰撞
		return false
	}

	// 有碰撞
	return true
}

分數(shù)繪制

游戲中應該在添加一個記錄成績的文本。

小鳥每次過一個障礙物讓其分數(shù) +1,考慮到小鳥一直是原地上下移動,是障礙物在一直移動,所以可以考慮通過障礙物的坐標來記錄分數(shù)。每當障礙物的 x 坐標 小于 小鳥的 x 坐標的時候,便讓成績+1即可。

func (g *Game) DrawBarrier(screen *ebiten.Image){
	if (gameState != 0){
		for _, o := range obstacles {
			if (o.X<200){
				if (!o.isScore){
					o.isScore = true
					Score++
				}
			}
		}
	}
}
func (g *Game) DrawScore(screen *ebiten.Image) {
	ebitenutil.DebugPrintAt(screen , Itoa(Score),100,100)
}

游戲暫停

這里是在游戲過程中,添加一個游戲暫停處理。

其實邏輯很簡單,添加一個按鍵監(jiān)聽,當按下暫停鍵后,讓 小鳥保持不動,障礙物不再移動即可。

那具體怎么處理呢? 小鳥的跳躍是通過按鍵響應來控制的,那么當調用這個按鍵響應的同時添加一個 判斷即可,判斷此時 是否暫停。

同樣的,障礙物移動的時候,也添加一個判斷,判斷此時是否暫停。

var isstop bool = false // 游戲是否暫停
//  判斷游戲是否暫停
	if ebiten.IsKeyPressed(ebiten.KeyS) && isPressStop && gameState ==1 && g.BirdState != 0{
		isPressStop = false
		if (!isstop){
			isstop = true
		}else {
			isstop = false
		}
	}
//  判斷是否暫停,然后判斷 小鳥此時 能否移動
	if !isstop { // 暫停
		g.LocationY = g.LocationY + float64(locationTemp)
	}
	// 判斷游戲是否暫停
	if ((g.LocationY != 482 || g.BirdState !=0) && !isstop && gameState != 0){ // 沒有暫停
		obojsaasdf(obstacles)
	}else if  ((g.LocationY != 482 || g.BirdState != 0 ) && isstop) { // 游戲暫停
			//fmt.Println("暫停了 ,障礙物不能動了")
	}

四、游戲結束階段

游戲結束

界面繪制

游戲結束后,需要繪制的就是 “GameOver” 和 這一局的分數(shù)了。還有就是讓障礙物不在移動,這個在下面的邏輯處理細講。

其實很簡單,就是獲取字符串內容,然后顯示在屏幕中間即可 - -

// 繪制 GameOver方法
func (g *Game) DrawGameOver(screen *ebiten.Image) {
	ebitenutil.DebugPrintAt(screen , "GameOver",400,210)
	ebitenutil.DebugPrintAt(screen , Itoa(Score),422,240)
}

// 繪制成績
func (g *Game) DrawScore(screen *ebiten.Image) {
	ebitenutil.DebugPrintAt(screen , Itoa(Score),100,100)
}

邏輯處理

當游戲暫停后,停止調用障礙物移動的方法就行了。

// 判斷游戲是否結束
	if ((g.LocationY != 482 || g.BirdState !=0) && !isstop && gameState != 0){ // 沒有結束
		obojsaasdf(obstacles)
	}else if  ((g.LocationY != 482 || g.BirdState != 0 ) && isstop) { // 游戲暫停
			//fmt.Println("暫停了 ,障礙物不能動了")
	}else if(g.LocationY == 482 || g.BirdState == 0 ) {  // 游戲結束
		gameState = 2
		g.DrawBegin(screen)
		g.DrawGameOver(screen)
	}

游戲重開

邏輯處理

添加一個按鍵響應,當按下重開鍵后,重新開始游戲。

重點是:要將信息全部初始化:初始化障礙物的切片,初始化小鳥狀態(tài) 和 坐標,初始化該局分數(shù)。

// 此時游戲結束 考慮是否重開
	if (gameState == 2){
		if ebiten.IsKeyPressed(ebiten.KeySpace) {   // 按壓空格后 重開
			Score = 0
			gameState = 1
			g.BirdState = 1
			g.LocationY = 200
			obstacles = obstacles[:0] // 清空 原來切片中的障礙物
			makeBarrier()
		}
	}

界面繪制

重新調用游戲開始階段的函數(shù)即可。

五、總結感想

做完這個游戲熟悉了 go 的基礎語法,增加了對代碼的手感。

到此這篇關于golang小游戲開發(fā)實戰(zhàn)之飛翔的小鳥的文章就介紹到這了,更多相關golang飛翔的小鳥小游戲內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Go語言中三種容器類型的數(shù)據(jù)結構詳解

    Go語言中三種容器類型的數(shù)據(jù)結構詳解

    在?Go?語言中,有三種主要的容器類型用于存儲和操作集合數(shù)據(jù)這篇文章主要為大家介紹了三者的使用與區(qū)別,感興趣的小伙伴可以跟隨小編一起學習一下
    2025-02-02
  • Go?tablewriter庫提升命令行輸出專業(yè)度實例詳解

    Go?tablewriter庫提升命令行輸出專業(yè)度實例詳解

    命令行工具大家都用過,如果是運維人員可能會編寫命令行工具來完成各種任務,命令行輸出的美觀和易讀性往往容易被忽視,很爛的輸出會讓人感覺不專業(yè),本文將介紹Go語言中牛逼的實戰(zhàn)工具tablewriter庫,使你在命令行輸出中展現(xiàn)出專業(yè)的一面
    2023-11-11
  • 詳解go中的引用類型

    詳解go中的引用類型

    這篇文章主要介紹了go中的引用類型,文中給大家提到了值類型和引用類型的區(qū)別,通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • GO 函數(shù)式選項模式(Functional Options Pattern)

    GO 函數(shù)式選項模式(Functional Options Pattern)

    Option模式支持傳遞多個參數(shù),并且在參數(shù)個數(shù)、類型發(fā)生變化時保持兼容性,任意順序傳遞參數(shù),下面給大家介紹GO 函數(shù)式選項模式(Functional Options Pattern)的相關知識,感興趣的朋友一起看看吧
    2021-10-10
  • Go?Web編程添加服務器錯誤和訪問日志

    Go?Web編程添加服務器錯誤和訪問日志

    這篇文章主要為大家介紹了Go?Web編程添加服務器錯誤日志和訪問日志的示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • go語言在請求http時加入自定義http header的方法

    go語言在請求http時加入自定義http header的方法

    這篇文章主要介紹了go語言在請求http時加入自定義http header的方法,實例分析了Go語言http請求的原理與操作技巧,需要的朋友可以參考下
    2015-03-03
  • golang實現(xiàn)http服務器處理靜態(tài)文件示例

    golang實現(xiàn)http服務器處理靜態(tài)文件示例

    這篇文章主要介紹了golang實現(xiàn)http服務器處理靜態(tài)文件的方法,涉及Go語言基于http協(xié)議處理文件的相關技巧,需要的朋友可以參考下
    2016-07-07
  • go mutex互斥鎖使用Lock和Unlock方法占有釋放資源

    go mutex互斥鎖使用Lock和Unlock方法占有釋放資源

    Go號稱是為了高并發(fā)而生的,在高并發(fā)場景下,勢必會涉及到對公共資源的競爭,當對應場景發(fā)生時,我們經(jīng)常會使用 mutex 的 Lock() 和 Unlock() 方法來占有或釋放資源,雖然調用簡單,但 mutex 的內部卻涉及挺多的,本文來好好研究一下
    2023-09-09
  • Go語言中三種不同md5計算方式的性能比較

    Go語言中三種不同md5計算方式的性能比較

    md5計算在我們日常工作的時候經(jīng)常能遇到,下面這篇文章主要介紹了Go語言中三種不同md5計算方式的性能比較,需要的朋友可以參考借鑒,下面來一起學習學習吧。
    2017-01-01
  • golang redigo發(fā)布訂閱使用的方法

    golang redigo發(fā)布訂閱使用的方法

    本文主要介紹了golang redigo發(fā)布訂閱使用的方法,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10

最新評論