前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Golang语言情怀--第131期 Go语言Ebiten引擎全栈游戏开发:第2节:Ebiten框架分析

Golang语言情怀--第131期 Go语言Ebiten引擎全栈游戏开发:第2节:Ebiten框架分析

作者头像
李海彬
发布于 2024-11-07 09:20:47
发布于 2024-11-07 09:20:47
18400
代码可运行
举报
文章被收录于专栏:Golang语言社区Golang语言社区
运行总次数:0
代码可运行

Ebiten实例分析:贪吃蛇

Ebiten是一个使用Go语言编程库,用于2D游戏开发,可以跨平台。本届开始讲解官方实例,实例熟悉后会给大家讲解实战游戏课。 贪吃蛇实例

源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import (
    "fmt"
    "image/color"
    "log"
    "math/rand/v2"

    "github.com/hajimehoshi/ebiten/v2"
    "github.com/hajimehoshi/ebiten/v2/ebitenutil"
    "github.com/hajimehoshi/ebiten/v2/inpututil"
    "github.com/hajimehoshi/ebiten/v2/vector"
)

const (
    screenWidth        = 640
    screenHeight       = 480
    gridSize           = 10
    xGridCountInScreen = screenWidth / gridSize
    yGridCountInScreen = screenHeight / gridSize
)

const (
    dirNone = iota
    dirLeft
    dirRight
    dirDown
    dirUp
)

type Position struct {
    X int
    Y int
}

type Game struct {
    moveDirection int
    snakeBody     []Position
    apple         Position
    timer         int
    moveTime      int
    score         int
    bestScore     int
    level         int
}

func (g *Game) collidesWithApple() bool {
    return g.snakeBody[0].X == g.apple.X &&
        g.snakeBody[0].Y == g.apple.Y
}

func (g *Game) collidesWithSelf() bool {
    for _, v := range g.snakeBody[1:] {
        if g.snakeBody[0].X == v.X &&
            g.snakeBody[0].Y == v.Y {
            return true
        }
    }
    return false
}

func (g *Game) collidesWithWall() bool {
    return g.snakeBody[0].X < 0 ||
        g.snakeBody[0].Y < 0 ||
        g.snakeBody[0].X >= xGridCountInScreen ||
        g.snakeBody[0].Y >= yGridCountInScreen
}

func (g *Game) needsToMoveSnake() bool {
    return g.timer%g.moveTime == 0
}

func (g *Game) reset() {
    g.apple.X = 3 * gridSize
    g.apple.Y = 3 * gridSize
    g.moveTime = 4
    g.snakeBody = g.snakeBody[:1]
    g.snakeBody[0].X = xGridCountInScreen / 2
    g.snakeBody[0].Y = yGridCountInScreen / 2
    g.score = 0
    g.level = 1
    g.moveDirection = dirNone
}

func (g *Game) Update() error {
    // Decide the snake's direction along with the user input.
    // A U-turn is forbidden here (e.g. if the snake is moving in the left direction, the snake cannot go to the right direction immediately).
    if inpututil.IsKeyJustPressed(ebiten.KeyArrowLeft) || inpututil.IsKeyJustPressed(ebiten.KeyA) {
        if g.moveDirection != dirRight {
            g.moveDirection = dirLeft
        }
    } else if inpututil.IsKeyJustPressed(ebiten.KeyArrowRight) || inpututil.IsKeyJustPressed(ebiten.KeyD) {
        if g.moveDirection != dirLeft {
            g.moveDirection = dirRight
        }
    } else if inpututil.IsKeyJustPressed(ebiten.KeyArrowDown) || inpututil.IsKeyJustPressed(ebiten.KeyS) {
        if g.moveDirection != dirUp {
            g.moveDirection = dirDown
        }
    } else if inpututil.IsKeyJustPressed(ebiten.KeyArrowUp) || inpututil.IsKeyJustPressed(ebiten.KeyW) {
        if g.moveDirection != dirDown {
            g.moveDirection = dirUp
        }
    } else if inpututil.IsKeyJustPressed(ebiten.KeyEscape) {
        g.reset()
    }

    if g.needsToMoveSnake() {
        if g.collidesWithWall() || g.collidesWithSelf() {
            g.reset()
        }

        if g.collidesWithApple() {
            g.apple.X = rand.IntN(xGridCountInScreen - 1)
            g.apple.Y = rand.IntN(yGridCountInScreen - 1)
            g.snakeBody = append(g.snakeBody, Position{
                X: g.snakeBody[len(g.snakeBody)-1].X,
                Y: g.snakeBody[len(g.snakeBody)-1].Y,
            })
            if len(g.snakeBody) > 10 && len(g.snakeBody) < 20 {
                g.level = 2
                g.moveTime = 3
            } else if len(g.snakeBody) > 20 {
                g.level = 3
                g.moveTime = 2
            } else {
                g.level = 1
            }
            g.score++
            if g.bestScore < g.score {
                g.bestScore = g.score
            }
        }

        for i := int64(len(g.snakeBody)) - 1; i > 0; i-- {
            g.snakeBody[i].X = g.snakeBody[i-1].X
            g.snakeBody[i].Y = g.snakeBody[i-1].Y
        }
        switch g.moveDirection {
        case dirLeft:
            g.snakeBody[0].X--
        case dirRight:
            g.snakeBody[0].X++
        case dirDown:
            g.snakeBody[0].Y++
        case dirUp:
            g.snakeBody[0].Y--
        }
    }

    g.timer++

    return nil
}

func (g *Game) Draw(screen *ebiten.Image) {
    for _, v := range g.snakeBody {
        vector.DrawFilledRect(screen, float32(v.X*gridSize), float32(v.Y*gridSize), gridSize, gridSize, color.RGBA{0x80, 0xa0, 0xc0, 0xff}, false)
    }
    vector.DrawFilledRect(screen, float32(g.apple.X*gridSize), float32(g.apple.Y*gridSize), gridSize, gridSize, color.RGBA{0xFF, 0x00, 0x00, 0xff}, false)

    if g.moveDirection == dirNone {
        ebitenutil.DebugPrint(screen, fmt.Sprintf("Press up/down/left/right to start"))
    } else {
        ebitenutil.DebugPrint(screen, fmt.Sprintf("FPS: %0.2f Level: %d Score: %d Best Score: %d", ebiten.ActualFPS(), g.level, g.score, g.bestScore))
    }
}

func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
    return screenWidth, screenHeight
}

func newGame() *Game {
    g := &Game{
        apple:     Position{X: 3 * gridSize, Y: 3 * gridSize},
        moveTime:  4,
        snakeBody: make([]Position, 1),
    }
    g.snakeBody[0].X = xGridCountInScreen / 2
    g.snakeBody[0].Y = yGridCountInScreen / 2
    return g
}

func main() {
    ebiten.SetWindowSize(screenWidth, screenHeight)
    ebiten.SetWindowTitle("Snake (Ebitengine Demo)")
    if err := ebiten.RunGame(newGame()); err != nil {
        log.Fatal(err)
    }
}

框架代码分析

在Ebiten框架中有一个3个函数: Layout函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (g *Game) Layout(outsideWidth, outsideHeight int) 

Draw函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (g *Game) Draw(screen *ebiten.Image) 

Update函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (g *Game) Update() error

以上3个函数是通过框架函数调用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ebiten.RunGame(newGame())

而ebiten.RunGame在框架底层:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// RunGame starts the main loop and runs the game.
// game's Update function is called every tick to update the game logic.
// game's Draw function is called every frame to draw the screen.
// game's Layout function is called when necessary, and you can specify the logical screen size by the function.
//
// If game implements FinalScreenDrawer, its DrawFinalScreen is called after Draw.
// The argument screen represents the final screen. The argument offscreen is an offscreen modified at Draw.
// If game does not implement FinalScreenDrawer, the default rendering for the final screen is used.
//
// game's functions are called on the same goroutine.
//
// On browsers, it is strongly recommended to use iframe if you embed an Ebitengine application in your website.
//
// RunGame must be called on the main thread.
// Note that Ebitengine bounds the main goroutine to the main OS thread by runtime.LockOSThread.
//
// Ebitengine tries to call game's Update function 60 times a second by default. In other words,
// TPS (ticks per second) is 60 by default.
// This is not related to framerate (display's refresh rate).
//
// RunGame returns error when 1) an error happens in the underlying graphics driver, 2) an audio error happens
// or 3) Update returns an error. In the case of 3), RunGame returns the same error so far, but it is recommended to
// use errors.Is when you check the returned error is the error you want, rather than comparing the values
// with == or != directly.
//
// If you want to terminate a game on desktops, it is recommended to return Termination at Update, which will halt
// execution without returning an error value from RunGame.
//
// The size unit is device-independent pixel.
//
// Don't call RunGame or RunGameWithOptions twice or more in one process.
func RunGame(game Game) error {
    return RunGameWithOptions(game, nil)
}

同时:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func RunGame(game Game) error {
    return RunGameWithOptions(game, nil)
}

中的函数参数:game Game:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Game defines necessary functions for a game.
type Game interface {
    // Update updates a game by one tick. The given argument represents a screen image.
    //
    // Update updates only the game logic and Draw draws the screen.
    //
    // You can assume that Update is always called TPS-times per second (60 by default), and you can assume
    // that the time delta between two Updates is always 1 / TPS [s] (1/60[s] by default). As Ebitengine already
    // adjusts the number of Update calls, you don't have to measure time deltas in Update by e.g. OS timers.
    //
    // An actual TPS is available by ActualTPS(), and the result might slightly differ from your expected TPS,
    // but still, your game logic should stick to the fixed time delta and should not rely on ActualTPS() value.
    // This API is for just measurement and/or debugging. In the long run, the number of Update calls should be
    // adjusted based on the set TPS on average.
    //
    // An actual time delta between two Updates might be bigger than expected. In this case, your game's
    // Update or Draw takes longer than they should. In this case, there is nothing other than optimizing
    // your game implementation.
    //
    // In the first frame, it is ensured that Update is called at least once before Draw. You can use Update
    // to initialize the game state.
    //
    // After the first frame, Update might not be called or might be called once
    // or more for one frame. The frequency is determined by the current TPS (tick-per-second).
    //
    // If the error returned is nil, game execution proceeds normally.
    // If the error returned is Termination, game execution halts, but does not return an error from RunGame.
    // If the error returned is any other non-nil value, game execution halts and the error is returned from RunGame.
    Update() error

    // Draw draws the game screen by one frame.
    //
    // The give argument represents a screen image. The updated content is adopted as the game screen.
    //
    // The frequency of Draw calls depends on the user's environment, especially the monitors refresh rate.
    // For portability, you should not put your game logic in Draw in general.
    Draw(screen *Image)

    // Layout accepts a native outside size in device-independent pixels and returns the game's logical screen
    // size in pixels. The logical size is used for 1) the screen size given at Draw and 2) calculation of the
    // scale from the screen to the final screen size.
    //
    // On desktops, the outside is a window or a monitor (fullscreen mode). On browsers, the outside is a body
    // element. On mobiles, the outside is the view's size.
    //
    // Even though the outside size and the screen size differ, the rendering scale is automatically adjusted to
    // fit with the outside.
    //
    // Layout is called almost every frame.
    //
    // It is ensured that Layout is invoked before Update is called in the first frame.
    //
    // If Layout returns non-positive numbers, the caller can panic.
    //
    // You can return a fixed screen size if you don't care, or you can also return a calculated screen size
    // adjusted with the given outside size.
    //
    // If the game implements the interface LayoutFer, Layout is never called and LayoutF is called instead.
    Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int)
}

以上就清楚了,和其他游戏引擎相似:update(),loaded(),start()等等,清楚了框架函数就知道贪吃蛇或者其他实例当中为什么有以上3个函数了。

贪吃蛇代码分析

定义结构信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const (
    screenWidth        = 640
    screenHeight       = 480
    gridSize           = 10
    xGridCountInScreen = screenWidth / gridSize
    yGridCountInScreen = screenHeight / gridSize
)

const (
    dirNone = iota
    dirLeft
    dirRight
    dirDown
    dirUp
)

type Position struct {
    X int
    Y int
}

type Game struct {
    moveDirection int
    snakeBody     []Position
    apple         Position
    timer         int
    moveTime      int
    score         int
    bestScore     int
    level         int
}

实例化贪吃蛇实例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func newGame() *Game {
    g := &Game{
        apple:     Position{X: 3 * gridSize, Y: 3 * gridSize},
        moveTime:  4,
        snakeBody: make([]Position, 1),
    }
    g.snakeBody[0].X = xGridCountInScreen / 2
    g.snakeBody[0].Y = yGridCountInScreen / 2
    return g
}

初始化贪吃蛇元素位置、设置相关信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (g *Game) Draw(screen *ebiten.Image) {
    for _, v := range g.snakeBody {
        vector.DrawFilledRect(screen, float32(v.X*gridSize), float32(v.Y*gridSize), gridSize, gridSize, color.RGBA{0x80, 0xa0, 0xc0, 0xff}, false)
    }
    vector.DrawFilledRect(screen, float32(g.apple.X*gridSize), float32(g.apple.Y*gridSize), gridSize, gridSize, color.RGBA{0xFF, 0x00, 0x00, 0xff}, false)

    if g.moveDirection == dirNone {
        ebitenutil.DebugPrint(screen, fmt.Sprintf("Press up/down/left/right to start"))
    } else {
        ebitenutil.DebugPrint(screen, fmt.Sprintf("FPS: %0.2f Level: %d Score: %d Best Score: %d", ebiten.ActualFPS(), g.level, g.score, g.bestScore))
    }
}

实时跟新游戏界面的元素和位置信息等:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (g *Game) Update() error {
    // Decide the snake's direction along with the user input.
    // A U-turn is forbidden here (e.g. if the snake is moving in the left direction, the snake cannot go to the right direction immediately).
    if inpututil.IsKeyJustPressed(ebiten.KeyArrowLeft) || inpututil.IsKeyJustPressed(ebiten.KeyA) {
        if g.moveDirection != dirRight {
            g.moveDirection = dirLeft
        }
    } else if inpututil.IsKeyJustPressed(ebiten.KeyArrowRight) || inpututil.IsKeyJustPressed(ebiten.KeyD) {
        if g.moveDirection != dirLeft {
            g.moveDirection = dirRight
        }
    } else if inpututil.IsKeyJustPressed(ebiten.KeyArrowDown) || inpututil.IsKeyJustPressed(ebiten.KeyS) {
        if g.moveDirection != dirUp {
            g.moveDirection = dirDown
        }
    } else if inpututil.IsKeyJustPressed(ebiten.KeyArrowUp) || inpututil.IsKeyJustPressed(ebiten.KeyW) {
        if g.moveDirection != dirDown {
            g.moveDirection = dirUp
        }
    } else if inpututil.IsKeyJustPressed(ebiten.KeyEscape) {
        g.reset()
    }

    if g.needsToMoveSnake() {
        if g.collidesWithWall() || g.collidesWithSelf() {
            g.reset()
        }

        if g.collidesWithApple() {
            g.apple.X = rand.IntN(xGridCountInScreen - 1)
            g.apple.Y = rand.IntN(yGridCountInScreen - 1)
            g.snakeBody = append(g.snakeBody, Position{
                X: g.snakeBody[len(g.snakeBody)-1].X,
                Y: g.snakeBody[len(g.snakeBody)-1].Y,
            })
            if len(g.snakeBody) > 10 && len(g.snakeBody) < 20 {
                g.level = 2
                g.moveTime = 3
            } else if len(g.snakeBody) > 20 {
                g.level = 3
                g.moveTime = 2
            } else {
                g.level = 1
            }
            g.score++
            if g.bestScore < g.score {
                g.bestScore = g.score
            }
        }

        for i := int64(len(g.snakeBody)) - 1; i > 0; i-- {
            g.snakeBody[i].X = g.snakeBody[i-1].X
            g.snakeBody[i].Y = g.snakeBody[i-1].Y
        }
        switch g.moveDirection {
        case dirLeft:
            g.snakeBody[0].X--
        case dirRight:
            g.snakeBody[0].X++
        case dirDown:
            g.snakeBody[0].Y++
        case dirUp:
            g.snakeBody[0].Y--
        }
    }

    g.timer++

    return nil
}

其他代码主要是游戏的逻辑,本实例都比较简单;暂时分析到这里;如果有不懂的可以留言,本期到这里,下期再见。

同学们,兴趣是最好的老师;只争朝夕,不负韶华!加油!

参考资料:

Go语言中文文档

http://www.golang.ltd/

Golang语言情怀

ID:wwwGolangLtd

www.Golang.Ltd

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-11-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Golang语言情怀 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Golang语言情怀--第133期 Go语言Ebiten引擎全栈游戏开发:第4节:sprites实例分析
sprite在游戏中我们常见的角色信息、面板UI都可以定义为sprite,翻译中文就是游戏中的精灵,也是前端游戏开发中需要掌握的知识点。
李海彬
2024/11/08
1490
Golang语言情怀--第133期 Go语言Ebiten引擎全栈游戏开发:第4节:sprites实例分析
Golang语言情怀--第136期 Go语言Ebiten引擎全栈游戏开发:第7节:video实例(直播技术基础)
video实例这个例子其实动视频、直播流、推拉流的技术人员,应该有种小惊喜;意思可以用Go来做视频监控,直播,视频播放器了,也不用自己再封装系统调用了,直播推拉流技术我一直再研究,有疑问的可以一起探讨。同时还是涉及到FFmpeg,如果不懂的可以和我交流
李海彬
2024/11/11
1150
Golang语言情怀--第136期 Go语言Ebiten引擎全栈游戏开发:第7节:video实例(直播技术基础)
Golang语言情怀--第131期 Go语言Ebiten引擎全栈游戏开发:第2节:Ebiten框架分析
Ebiten是一个使用Go语言编程库,用于2D游戏开发,可以跨平台。本届开始讲解官方实例,实例熟悉后会给大家讲解实战游戏课。 贪吃蛇实例
李海彬
2024/11/07
1840
Golang语言情怀--第131期 Go语言Ebiten引擎全栈游戏开发:第2节:Ebiten框架分析
Go语言的文件处理:详细指南
该代码示例展示了如何逐行读取名为example.txt的文件,并将每一行打印到控制台。
数字扫地僧
2024/06/25
1840
Golang语言情怀--第140期 Go语言Ebiten引擎《荒野坦克大战》开发:游客功能UI完成
本节主要是完成:游客登录 涉及到的知识点,主要是按钮,背景相关。 以下代码是个按钮实例,大家可以简单看下:
李海彬
2024/11/15
1670
Golang语言情怀--第140期 Go语言Ebiten引擎《荒野坦克大战》开发:游客功能UI完成
[译]Go语言常用文件操作汇总
Go官方提供的文件操作标准库分散在os、ioutil等多个包中,里面有非常多的方法涵盖了文件操作的所有场景,不过因为我平时开发过程中需要直接操作文件的场景其实并不多,在加上Go标准库的文档太难搜索,每次遇到要使用文件函数时都是直接Google查对应的函数。偶然查到国外一个人在2015年写的博客,他用常用的文件函数汇总了30个文件操作场景,包括四大类:基本操作、读写操作、文件压缩、其他操作。每一个文件操作都给了代码示例。写的非常好,强烈推荐你阅读一下,浏览一下它的目录,然后放到收藏夹里吃灰,万一哪天用到了还能拿来参考一下。
KevinYan
2020/10/10
1.2K0
Golang语言情怀--第137期 Go语言Ebiten引擎全栈游戏开发:第8节:animation实例
游戏中的角色的行走、跑、攻击等等,都是动画来实现的;动画在游戏引擎中也是比不可少的一部分,成熟的游戏引擎针对2D开发游戏而言主要是满足spine动画,spine动画目前是使用比较广泛的,如果不了解的可以去网上查下。如果有不懂的也可以咨询我,《荒野坦克大战》中的部分动画都是我用spine来做的。
李海彬
2024/11/13
2050
Golang语言情怀--第137期 Go语言Ebiten引擎全栈游戏开发:第8节:animation实例
Golang语言情怀--第138期 Go语言Ebiten引擎全栈游戏开发:第8节:《荒野坦克大战》PC端移植-资源整理
李海彬
2024/11/13
1430
Golang语言情怀--第138期 Go语言Ebiten引擎全栈游戏开发:第8节:《荒野坦克大战》PC端移植-资源整理
Golang语言情怀--第132期 Go语言Ebiten引擎全栈游戏开发:第3节:游戏手柄事件监听实例分析
首先,游戏手柄操作游戏是部分主机游戏必备,最近比较火的黑神话·悟空等等,手机游戏也可以使用手柄的,目前我们的坦克对决单机版就支持了手柄,后续完成给大家提供下载。直接上代码,如下:
李海彬
2024/11/07
1320
Golang语言情怀--第132期 Go语言Ebiten引擎全栈游戏开发:第3节:游戏手柄事件监听实例分析
influx测试——单条读性能很差,大约400条/s,批量写性能很高,7万条/s,总体说来适合IOT数据批量存,根据tag查和过滤场景,按照时间顺序返回…
需要将InfluxDB的源码放入 go/src/github.com/influxdata 目录
全栈程序员站长
2022/07/20
3450
Golang语言情怀--第144期 Go语言Ebiten引擎《荒野坦克大战》开发PC端:大厅UI编码
视频地址:https://mpvideo.qpic.cn/0b2e5qab6aaasmadbqr75ftvb3gdd7waahya.f10002.mp4?
李海彬
2024/11/25
820
Golang语言情怀--第144期 Go语言Ebiten引擎《荒野坦克大战》开发PC端:大厅UI编码
一起用Go做一个小游戏(上)
最近偶然看到一个Go语言库,口号喊出“一个超级简单(dead simple)的2D游戏引擎”,好奇点开了它的官网。
用户7731323
2023/02/14
1.1K0
一起用Go做一个小游戏(上)
Golang语言情怀--第134期 Go语言Ebiten引擎全栈游戏开发:第5节:fonts实例分析
fonts字体也是游戏引擎中比较重要的一个知识点,游戏本身需要视觉感受,不可能像文章一样,游戏中都是宋体、楷体等,所以游戏中fonts是必须要了解和掌握的内容。 实例代码,如下:
李海彬
2024/11/11
1110
Golang语言情怀--第134期 Go语言Ebiten引擎全栈游戏开发:第5节:fonts实例分析
Golang语言情怀--第139期 Go语言Ebiten引擎全栈游戏开发:第9节:《荒野坦克大战》PC端移植-游客功能UI开发
涉及到的知识点,主要是按钮,背景相关。以下代码是个按钮增加图实例,大家可以简单看下代码:
李海彬
2024/11/15
660
Golang语言情怀--第139期 Go语言Ebiten引擎全栈游戏开发:第9节:《荒野坦克大战》PC端移植-游客功能UI开发
一起用Go做一个小游戏(下)
使用file2byteslice包我们可以将图片和config.json文件打包进二进制程序中,之后编译生成一个二进制程序。然后拷贝这一个文件即可,不用再拷贝图片和其他配置文件了。
用户7731323
2023/02/14
4670
一起用Go做一个小游戏(下)
一起用Go做一个小游戏(中)
上一篇文章还留了个尾巴,细心的同学应该发现了:飞船可以移动出屏幕!!!现在我们就来限制一下飞船的移动范围。我们规定飞船可以左右超过半个身位,如下图所示:
用户7731323
2023/02/14
6290
一起用Go做一个小游戏(中)
Golang语言情怀-第67期 Go 语言标准库翻译 crypto/sha1
import "crypto/sha1" sha1包实现了SHA1哈希算法 Constants func Sum(data []byte) [Size]byte func New() hash.Hash New Sum Constants const BlockSize = 64 SHA1的块大小。 const Size = 20 SHA1校验和的字节数。 func New func New() hash.Hash 返回一个新的使用SHA1校验的hash.Hash接口。 package main
李海彬
2021/03/09
4350
Golang语言情怀--第142期 Go语言Ebiten引擎《荒野坦克大战》开发PC端:调试访问Proxy服务器
上一节链接登录服务器成功,通过登录服务器获取到token数据;token数据的作用是通过websocket在游戏服务器验证合法性;本节实现的是客户端链接代理服务器成功,主要思路是客户单通过代理Proxy服务器访问内网游戏服务器。
李海彬
2024/11/21
920
Golang语言情怀--第142期 Go语言Ebiten引擎《荒野坦克大战》开发PC端:调试访问Proxy服务器
嵌入式linux之go语言开发(八)存储模块的封装(二)
由于在终端上,不需要执行复杂的sql查询,多表级联查询等。就是用来存储记录的,因此设计为存储到表里的都为二进制的字节流。
杨永贞
2020/08/04
5090
Golang语言情怀--第141期 Go语言Ebiten引擎《荒野坦克大战》开发PC端:调试登录服务器完成
本节主要调试登录,客户端链接登录服务器,目前已经测试完成,并且成功拿点登录服务器的token数据,如下图:
李海彬
2024/11/19
920
Golang语言情怀--第141期 Go语言Ebiten引擎《荒野坦克大战》开发PC端:调试登录服务器完成
推荐阅读
Golang语言情怀--第133期 Go语言Ebiten引擎全栈游戏开发:第4节:sprites实例分析
1490
Golang语言情怀--第136期 Go语言Ebiten引擎全栈游戏开发:第7节:video实例(直播技术基础)
1150
Golang语言情怀--第131期 Go语言Ebiten引擎全栈游戏开发:第2节:Ebiten框架分析
1840
Go语言的文件处理:详细指南
1840
Golang语言情怀--第140期 Go语言Ebiten引擎《荒野坦克大战》开发:游客功能UI完成
1670
[译]Go语言常用文件操作汇总
1.2K0
Golang语言情怀--第137期 Go语言Ebiten引擎全栈游戏开发:第8节:animation实例
2050
Golang语言情怀--第138期 Go语言Ebiten引擎全栈游戏开发:第8节:《荒野坦克大战》PC端移植-资源整理
1430
Golang语言情怀--第132期 Go语言Ebiten引擎全栈游戏开发:第3节:游戏手柄事件监听实例分析
1320
influx测试——单条读性能很差,大约400条/s,批量写性能很高,7万条/s,总体说来适合IOT数据批量存,根据tag查和过滤场景,按照时间顺序返回…
3450
Golang语言情怀--第144期 Go语言Ebiten引擎《荒野坦克大战》开发PC端:大厅UI编码
820
一起用Go做一个小游戏(上)
1.1K0
Golang语言情怀--第134期 Go语言Ebiten引擎全栈游戏开发:第5节:fonts实例分析
1110
Golang语言情怀--第139期 Go语言Ebiten引擎全栈游戏开发:第9节:《荒野坦克大战》PC端移植-游客功能UI开发
660
一起用Go做一个小游戏(下)
4670
一起用Go做一个小游戏(中)
6290
Golang语言情怀-第67期 Go 语言标准库翻译 crypto/sha1
4350
Golang语言情怀--第142期 Go语言Ebiten引擎《荒野坦克大战》开发PC端:调试访问Proxy服务器
920
嵌入式linux之go语言开发(八)存储模块的封装(二)
5090
Golang语言情怀--第141期 Go语言Ebiten引擎《荒野坦克大战》开发PC端:调试登录服务器完成
920
相关推荐
Golang语言情怀--第133期 Go语言Ebiten引擎全栈游戏开发:第4节:sprites实例分析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档