Golang 简单的碰撞检测游戏(ebiten)

在这里插入图片描述

简单的碰撞检测游戏

游戏功能:
1、空格键发射红色块,红色块遇到左、右、上边界反弹,遇到下边界游戏结束。
2、左右键移动白色块接红色块,每接到一次加一分。
3、每5秒钟随机生成一个黄色块,红色块触碰到黄色块时,黄色块消失并加十分,黄色块同时最多存在5个

逛博客时看到了一个golang的2D游戏框架ebiten,所以写了个简单游戏来熟悉ebiten流程。
源码地址

main.go

package main

import (
	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/ebitenutil"
	"github.com/vua/vGame/component"
	"image/color"
	"log"
	"math/rand"
	"time"
)

// Game implements ebiten.Game interface.
type Game struct {
	recv   *component.Square   //白色块
	ball   *component.Square   //红色块
	awards []*component.Square //黄色块
	cls    chan int       
	w      int                  
	h      int
}

// Update proceeds the game state.
// Update is called every tick (1/60 [s] by default).
var a byte = 0xff

func (g *Game) Update() error {
	//发射红色块
	if ebiten.IsKeyPressed(ebiten.KeySpace) {
		g.ball.IsRun = true
	}
	//红色块已发射
	if g.ball.IsRun {
		//边界碰撞检测
		g.ball.CollisionDetection(g.recv, float64(g.w), float64(g.h))
		//吃黄块
		g.ball.HitDetection(&g.awards)
	}
	//左右移动白色块
	if ebiten.IsKeyPressed(ebiten.KeyLeft) {
		g.recv.Move(g.w, -5, g.ball)
	}
	if ebiten.IsKeyPressed(ebiten.KeyRight) {
		g.recv.Move(g.w, 5, g.ball)
	}
	/*if !g.ball.IsAlive() {
		close(g.cls)
		return errors.New("Game Over")
	}*/
	return nil
}

// Draw draws the game screen.
// Draw is called every frame (typically 1/60[s] for 60Hz display).
func (g *Game) Draw(screen *ebiten.Image) {
	//绘制白色块
	screen.DrawImage(g.recv.Image, g.recv.Opts)
	//绘制红色块
	screen.DrawImage(g.ball.Image, g.ball.Opts)
	//绘制黄色块
	for _, i := range g.awards {
		screen.DrawImage(i.Image, i.Opts)
	}
	//红色块落地
	if !g.ball.IsAlive() {
		ebitenutil.DebugPrint(screen, "Game Over")
	} else {
	//显示当前分数
		ebitenutil.DebugPrint(screen, "Score:"+g.ball.GetScore())
	}
	// Write your game's rendering.
}

// Layout takes the outside size (e.g., the window size) and returns the (logical) screen size.
// If you don't have to adjust the screen size with the outside size, just return a fixed size.
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
	return g.w, g.h
}
//每5秒生成一个黄色块
func (g *Game) awardGenerator() {
	ticker := time.NewTicker(5 * time.Second)
	for {
		select {
		case <-ticker.C:
			if !g.ball.IsAlive() {
				return
			}
			if len(g.awards) == 5 {
				g.awards = g.awards[1:5]
			}
			g.awards = append(g.awards, component.NewSquare(Yellow, 5, 5, float64(rand.Intn(300)+10), float64(rand.Intn(200)+10), 0))
		case <-g.cls:
			return
		}
	}
}

var (
	Red    = color.RGBA{0xff, 0x00, 0x00, 0xff}
	Yellow = color.RGBA{0xff, 0xff, 0x00, 0xff}
	White  = color.RGBA{0xff, 0xff, 0xff, 0xff}
)

func main() {
	game := &Game{
		recv:   component.NewSquare(White, 5, 40, 140, 230, 0),
		ball:   component.NewSquare(Red, 5, 5, 157.5, 225, 3),
		awards: make([]*component.Square, 0),
		cls:    make(chan int, 0),
		w:      320,
		h:      240,
	}
	//启动黄色块生成器
	go game.awardGenerator()
	// Specify the window size as you like. Here, a doubled size is specified.
	ebiten.SetWindowSize(640, 480)
	ebiten.SetWindowTitle("vGame")
	// Call ebiten.RunGame to start your game loop.
	if err := ebiten.RunGame(game); err != nil {
		log.Fatal(err)
	}
}

square.go

package component

import (
	"github.com/hajimehoshi/ebiten/v2"
	"image/color"
	"math"
	"math/rand"
	"strconv"
)

type Square struct {
	bgc   color.RGBA   //颜色
	h     float64      //高
	w     float64      //宽
	x     float64      //左上点X坐标
	y     float64      //左上点Y坐标
	step  float64      //移动速度(专用于红色块)
	angle float64      //发射角度(专用于红色块)
	stepX float64      //X方向移动速度(专用于红色块)
	stepY float64      //Y方向移动速度(专用于红色块)
	score int          //得分(专用于红色块)
	alive bool         //是否落地(专用于红色块)
	IsRun bool         //是否已发射(专用于红色块)
	Image *ebiten.Image    //ebiten图像(色块)对象
	Opts  *ebiten.DrawImageOptions    //主要用来控制色块位置
}

func NewSquare(bgc color.RGBA, h, w int, x, y, step float64) *Square {
	image := ebiten.NewImage(w, h)  //创建色块块
	image.Fill(bgc)                 //上色
	opts := &ebiten.DrawImageOptions{}   
	opts.GeoM.Translate(x, y)       //移动到初始位置
	angle := float64(rand.Intn(120) + 30)   //设置初始发射角度
	return &Square{
		bgc:   bgc,
		h:     float64(h),
		w:     float64(w),
		x:     x,
		y:     y,
		step:  step,
		angle: angle,
		stepX: step * math.Cos(angle),   //根据角度和速度计算X,Y方向速度
		stepY: -step * math.Sin(angle),
		alive: true,
		Image: image,
		Opts:  opts,
	}
}
//获取得分(专用于红色块)
func (s *Square) GetScore() string{  
	return strconv.Itoa(s.score)
}
//是否落地(专用于红色块)
func (s *Square) IsAlive() bool{
	return s.alive
}
//边界碰撞检测(专用于红色块)
func (s *Square) CollisionDetection(recv *Square,w,h float64) {
	x, y := s.x+s.stepX, s.y+s.stepY
	tx,ty:=s.stepX,s.stepY
	//碰撞垂直边 (-stepX,stepY)
	//碰撞水平边 (stepX,-stepY)
	if x<=0 {
		tx=-s.x
		ty=tx*math.Tan(s.angle)
		s.stepX*=-1
	} else if x+s.w>=w{
		tx=320-s.w-s.x
		ty=tx*math.Tan(s.angle)
		s.stepX*=-1
	}else if y<=0 {
		ty=-s.y
		tx=ty/math.Tan(s.angle)
		s.stepY*=-1
	}else if y+s.h >= recv.y&& y<= recv.y+recv.h&& x <= recv.w+recv.x && x+s.w >= recv.x {
		s.score++
		ty=recv.y-s.y-s.w
		tx=ty/math.Tan(s.angle)
		if s.stepY*s.stepX<0 {
			tx*=-1
		}
		s.stepY*=-1
	}else if y+s.h>=h {
		s.alive=false
		return
	}
	s.x+=tx
	s.y+=ty
	s.Opts.GeoM.Translate(tx, ty)
}
//与黄色块的碰撞检测(专用于红色块)
func (s *Square) HitDetection(awards *[]*Square){
	for i:=0;i<len(*awards);i++{
		award:=(*awards)[i]
		if math.Abs(s.x-award.x)<=s.w&&math.Abs(s.y-award.y)<=s.h {
			*awards=append((*awards)[0:i],(*awards)[i+1:]...)
			i--
			s.score+=10
		}
	}
}
//左右移动(用于白色块,和未发射的红色块)
func (s *Square) Move(w int,step float64,boll *Square) {
	W:=float64(w)
	if s.x+step < 0 {
		s.Opts.GeoM.Translate(0-s.x, 0)
		if !boll.IsRun{
			boll.Opts.GeoM.Translate(0-s.x, 0)
			boll.x+=-s.x
		}
		s.x = 0
		return
	}
	if s.x+step > W-s.w {
		s.Opts.GeoM.Translate(W-s.w-s.x, 0)
		if !boll.IsRun{
			boll.Opts.GeoM.Translate(W-s.w-s.x, 0)
			boll.x+=W-s.w-s.x
		}
		s.x = W - s.w
		return
	}
	s.x += step
	s.Opts.GeoM.Translate(step, 0)
	if !boll.IsRun {
		boll.Opts.GeoM.Translate(step, 0)
		boll.x+=step
	}
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Golang 中,可以使用 `github.com/gorilla/websocket` 包提供的 `*websocket.Conn` 对象来处理 WebSocket 连接。 要检测 WebSocket 连接状态,可以使用 `*websocket.Conn` 对象的 `CloseHandler()` 方法。该方法接收一个函数作为参数,当连接关闭时会调用该函数。我们可以在该函数中检测连接状态。 下面是一个使用 `CloseHandler()` 方法检测 WebSocket 连接状态的示例代码: ```go conn, _, err := websocket.DefaultDialer.Dial("ws://localhost:8080/ws", nil) if err != nil { log.Fatal("dial error:", err) } // 监听连接关闭事件 closeChan := make(chan struct{}) conn.SetCloseHandler(func(code int, text string) error { closeChan <- struct{}{} return nil }) // 向服务器发送消息 err = conn.WriteMessage(websocket.TextMessage, []byte("hello")) if err != nil { log.Println("write error:", err) } // 检测连接状态 for { select { case <-closeChan: log.Println("connection closed") return default: _, _, err := conn.ReadMessage() if err != nil { log.Println("read error:", err) closeChan <- struct{}{} return } log.Println("connection is alive") time.Sleep(time.Second) } } ``` 在上面的代码中,我们首先通过 `websocket.DefaultDialer.Dial()` 方法建立一个 WebSocket 连接。 然后,我们使用 `conn.SetCloseHandler()` 方法定义一个回调函数来处理连接关闭事件。当连接关闭时,该回调函数会向 `closeChan` 通道发送一个空结构体。 在主循环中,我们使用 `conn.ReadMessage()` 方法读取服务器发送的消息。如果读取失败,说明连接已关闭,我们也需要向 `closeChan` 通道发送一个空结构体,从而触发连接关闭事件。 在主循环中,我们还使用 `default:` 分支来定期检测连接状态。如果连接仍然存活,我们会打印一条日志来表示连接仍然存活。 以上就是一个简单的使用 `CloseHandler()` 方法检测 WebSocket 连接状态的示例代码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值