golang游戏开发学习笔记-开发一个简单的2D游戏(基础篇)

2.人物运动图(只展示第一帧)

在这里插入图片描述

2.方块纹理图

在这里插入图片描述

将资源准备完成之后,就能开始代码的开发了

五.开始实现!

1.资源管理

在上一篇文章中我们将纹理和着色器分别封装成了两个类,这里我们创建一个资源管理类对这两个类进行管理,由于golang中是没有静态变量的,需要用包内变量对其进行模拟

shader.go

package resource

import(

“github.com/go-gl/gl/v4.1-core/gl”

“github.com/go-gl/mathgl/mgl32”

“strings”

“fmt”

)

type Shader struct{

ID uint32

}

func Compile(vertexString, fragmentString string) *Shader{

vertexShader,err := compile(vertexString+“\x00”, gl.VERTEX_SHADER)

if err != nil{

panic(err)

}

fragmentShader,err := compile(fragmentString+“\x00”, gl.FRAGMENT_SHADER)

if err != nil{

panic(err)

}

progID := gl.CreateProgram()

gl.AttachShader(progID, vertexShader)

gl.AttachShader(progID, fragmentShader)

gl.LinkProgram(progID)

gl.DeleteShader(vertexShader)

gl.DeleteShader(fragmentShader)

return &Shader{ ID: progID}

}

func (shader *Shader) Use(){

gl.UseProgram(shader.ID)

}

func (shader *Shader) SetBool(name string, value bool){

var a int32 = 0;

if(value){

a = 1

}

gl.Uniform1i(gl.GetUniformLocation(shader.ID, gl.Str(name + “\x00”)), a)

}

func (shader *Shader) SetInt(name string, value int32){

gl.Uniform1i(gl.GetUniformLocation(shader.ID, gl.Str(name + “\x00”)), value)

}

func (shader *Shader) SetFloat(name string, value float32){

gl.Uniform1f(gl.GetUniformLocation(shader.ID, gl.Str(name + “\x00”)), value)

}

func (shader *Shader) SetMatrix4fv(name string, value *float32){

gl.UniformMatrix4fv(gl.GetUniformLocation(shader.ID, gl.Str(name + “\x00”)), 1,false,value)

}

func (shader *Shader) SetVector3f(name string, vec3 mgl32.Vec3){

gl.Uniform3f(gl.GetUniformLocation(shader.ID, gl.Str(name + “\x00”)), vec3[0], vec3[1], vec3[2]);

}

func compile(sourceString string, shaderType uint32)(uint32, error){

shader := gl.CreateShader(shaderType)

source, free := gl.Strs(sourceString)

gl.ShaderSource(shader, 1, source, nil)

free()

gl.CompileShader(shader)

var status int32

gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)

if status == gl.FALSE {

var logLength int32

gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)

log := strings.Repeat(“\x00”, int(logLength+1))

gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))

return 0, fmt.Errorf(“failed to compile %v: %v”, source, log)

}

return shader, nil

}

shader类从给定的两个字符串中编译出顶点着色器和片段着色器,同时提供几个用于设置uniform变量的函数,唯一要说明的是字符串尾部要添加0X00来标识结尾(发现只有golang需要这样)

两个着色器程序

着色器程序的用途大家可以在参考资料或者之前的文章中找到详细说明,这里只从代码上大概讲解一下。需要说明的是,着色器程序需要结合具体要绘制的顶点来看,在这个2D游戏中,所有的元素都是由两个三角形组成的矩形构成的,因此在不使用EBO的情况下需要六个顶点

顶点

vertices := []float32{

0.0, 1.0, 0.0, 1.0,

1.0, 0.0, 1.0, 0.0,

0.0, 0.0, 0.0, 0.0,

0.0, 1.0, 0.0, 1.0,

1.0, 1.0, 1.0, 1.0,

1.0, 0.0, 1.0, 0.0,

}

由于我们开发的是2D游戏,所有顶点的z分量都为0,所以用顶点中每一行的前两位作为顶点坐标,后两位作为纹理坐标,并将顶点坐标和纹理坐标合为一个变量传入着色器中

顶点着色器

#version 410 core

layout (location = 0) in vec4 vertex;

out vec2 TexCoords;

uniform mat4 model;

uniform mat4 projection;

uniform mat4 view;

uniform int reverseX;

void main()

{ if(reverseX == 1){

TexCoords = vertex.zw;

}else{

TexCoords = vec2(1 - vertex.z, vertex.w);

}

gl_Position = projection * view * model * vec4(vertex.x, vertex.y, 0.0, 1.0);

}

着色器程序中modelprojectionview这三个变量用于将物体从局部空间变换到世界空间并转换为用户视角,可以理解为负责对物体进行平移,变形,对画面进行裁剪的工具。在前面的文章中已经讲过,不再赘述。vertex变量就是前面顶点数据中的每一行,其中前两位代表顶点坐标,后两位代表纹理坐标,reverseX变量为unifom类型,同样从外部传入,负责控制图像是否沿Y轴方向进行镜像,具体作用后面会讲到

片段着色器

#version 410 core

in vec2 TexCoords;

out vec4 FragColor;

uniform sampler2D image;

uniform vec3 spriteColor;

void main()

{

vec4 texColor = texture(image, TexCoords);

if(texColor.a < 0.1)

discard;

FragColor = vec4(spriteColor, 1.0) * texColor;

}

片段着色器与上一篇文章的基本相同,唯一区别是加入了一个判断,在图像区域的透明度小于0.1的时候,会放弃对这片区域的渲染。

texture2D.go

package resource

import(

“os”

“image”

“image/png”

“image/draw”

“errors”

“github.com/go-gl/gl/v4.1-core/gl”

)

type Texture2D struct{

ID uint32

TEXTUREINDEX uint32

}

func NewTexture2D(file string, TEXTUREINDEX uint32) *Texture2D{

imgFile, err := os.Open(file)

if err != nil {

panic(err)

}

img, err := png.Decode(imgFile)

if err != nil {

panic(err)

}

rgba := image.NewRGBA(img.Bounds())

if rgba.Stride != rgba.Rect.Size().X*4 {

panic(errors.New(“unsupported stride”))

}

draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src)

var textureID uint32

gl.GenTextures(1, &textureID)

gl.BindTexture(gl.TEXTURE_2D, textureID)

gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)

gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)

gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)

gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)

gl.TexImage2D(

gl.TEXTURE_2D,

0,

gl.RGBA,

int32(rgba.Rect.Size().X),

int32(rgba.Rect.Size().Y),

0,

gl.RGBA,

gl.UNSIGNED_BYTE,

gl.Ptr(rgba.Pix))

gl.BindTexture(gl.TEXTURE_2D, 0);

return &Texture2D{ ID: textureID,TEXTUREINDEX:TEXTUREINDEX}

}

func (texture *Texture2D) Use(){

gl.ActiveTexture(texture.TEXTUREINDEX)

gl.BindTexture(gl.TEXTURE_2D, texture.ID)

}

texture2D类用于解析png格式的图片并创建纹理绑定到上下文中

resource.go

package resource

import (

“io/ioutil”

)

var (

textures = make(map[string]*Texture2D)

shaders = make(map[string]*Shader)

)

func LoadShader(vShaderFile, fShaderFile, name string){

vertexString, err := ioutil.ReadFile(vShaderFile)

if err != nil{

panic(err)

}

fragmentString, err := ioutil.ReadFile(fShaderFile)

if err != nil{

panic(err)

}

shaders[name] = Compile(string(vertexString), string(fragmentString))

}

func GetShader(name string) *Shader{

return shaders[name]

}

func LoadTexture(TEXTUREINDEX uint32, file, name string){

texture := NewTexture2D(file, TEXTUREINDEX)

textures[name] = texture

}

func GetTexture(name string) *Texture2D{

return textures[name]

}

负责加载纹理和着色器的管理类

2.游戏对象

首先想一想游戏内所有元素都有的属性有哪些,并对其进行封装,创建一个游戏对象基类

gameObj.go

package model

import(

“game2D/resource”

“game2D/sprite”

“github.com/go-gl/mathgl/mgl32”

)

type GameObj struct{

texture *resource.Texture2D

x float32

y float32

size *mgl32.Vec2

rotate float32

color *mgl32.Vec3

isXReverse int32

}

func(gameObj GameObj) GetPosition()mgl32.Vec2{

return mgl32.Vec2{gameObj.x, gameObj.y}

}

func(gameObj *GameObj) SetPosition(position mgl32.Vec2){

gameObj.x = position[0]

gameObj.y = position[1]

}

func(gameObj GameObj) GetSize()mgl32.Vec2{

return mgl32.Vec2{gameObj.size[0], gameObj.size[1]}

}

func(gameObj *GameObj) Draw(renderer *sprite.SpriteRenderer){

renderer.DrawSprite(gameObj.texture, &mgl32.Vec2{gameObj.x,gameObj.y}, gameObj.size, gameObj.rotate, gameObj.color,gameObj.isXReverse)

}

func(gameObj *GameObj) ReverseX(){

gameObj.isXReverse = -1

}

func(gameObj *GameObj) ForWardX(){

gameObj.isXReverse = 1

}

func NewGameObj(texture *resource.Texture2D, x, y float32, size *mgl32.Vec2, rotate float32, color *mgl32.Vec3) *GameObj{

return &GameObj{texture:texture,

x:x,

y:y,

size:size,

rotate:rotate,

color:color,

isXReverse:1}

}

一个游戏内的对象一定会有那些属性?位置,大小,外观(纹理),颜色,和旋转的角度。同时为了效率,加入一个标识是否镜像的变量。有了这些属性,我们就能直接将其绘制到屏幕上。

3.精灵

我们已经有了可供绘制游戏对象,接下来自然就是将他绘制到屏幕上了,创建一个精灵绘制类

SpriteRenderer .go

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注:Python)

班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-2ddrG3X1-1713581229416)]

[外链图片转存中…(img-InPa1hqi-1713581229417)]

[外链图片转存中…(img-6vwo0qVJ-1713581229418)]

[外链图片转存中…(img-iAcTYSw6-1713581229419)]

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注:Python)

  • 13
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值