Collision doesn't work yet.

main
Sean Hickey 2021-12-30 20:47:01 -08:00
parent eec2d2c3d1
commit 6a982acd5a
13 changed files with 354 additions and 119 deletions

40
main.go
View File

@ -8,7 +8,6 @@ import (
"runtime/pprof"
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/breakout"
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/gamewindow"
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/globjects"
gl "github.com/go-gl/gl/v3.1/gles2"
"github.com/go-gl/mathgl/mgl32"
@ -54,7 +53,7 @@ func runOpenGL() {
sdlSettings()
gameWindow, err := gamewindow.NewGameWindow(gameTitle)
gameWindow, err := breakout.NewGameWindow(gameTitle)
if err != nil {
log.Fatal(err)
}
@ -72,7 +71,7 @@ func runOpenGL() {
log.Printf("OpenGL Version: %v\n", gl.GoStr(glVersion))
// Then we can setup the OpenGL specific stuff in the game window
gameWindow.GLInit()
gameWindow.GLInitDefault()
sunLight := globjects.NewPointLight(mgl32.Vec3{-10, 10, 30})
sunLight.GLInit(gameWindow.GLProgram)
@ -90,35 +89,42 @@ func runOpenGL() {
// Brick Targets
targets := breakout.NewTargets(4, 5, mgl32.Vec3{0, 6, 0})
targets.GLInit(gameWindow.GLProgram)
gameWindow.SetTargets(targets)
for _, brick := range targets.Bricks {
gameWindow.AddObject(brick)
gameWindow.AddObject(brick.Box)
}
positiveBoundary := targets.TopRight
negativeBoundary := positiveBoundary.Mul(-1)
gameWindow.PositiveBoundary = positiveBoundary
gameWindow.NegativeBoundary = negativeBoundary
// Bounding Box
boundingBoxMaterial := globjects.NewMaterial()
boundingBoxMaterial.Color = mgl32.Vec4{0.3, 0.3, 0.3, 1}
boundingBox := breakout.NewBoundingBox(negativeBoundary, positiveBoundary, boundingBoxMaterial)
boundingBox.GLInit(gameWindow.GLProgram)
gameWindow.AddObject(boundingBox)
gameWindowAABB := globjects.NewAABB(targets.TopRight, targets.TopRight.Mul(-1))
gameWindow.AABB = gameWindowAABB
// Side Walls
sideWallsMaterial := globjects.NewMaterial()
sideWallsMaterial.Color = mgl32.Vec4{0.3, 0.3, 0.3, 1}
sideWalls := breakout.NewSideWalls(gameWindowAABB, sideWallsMaterial)
sideWalls.GLInit(gameWindow.GLProgram)
gameWindow.SetSideWalls(sideWalls)
for _, wall := range sideWalls.Boxes {
gameWindow.AddObject(wall)
}
// Paddle
paddleMaterial := globjects.NewMaterial()
paddleMaterial.Color = mgl32.Vec4{0, 1, 0, 1}
paddle := globjects.NewBox(6.0, 1.0, 4.0, mgl32.Vec3{0, -6, 0}, paddleMaterial)
paddleBox := globjects.NewBox(6.0, 1.0, 4.0, mgl32.Vec3{0, -8, 0}, paddleMaterial)
paddle := breakout.NewPaddle(paddleBox)
paddle.GLInit(gameWindow.GLProgram)
gameWindow.AddObject(paddle)
gameWindow.SetPaddle(paddle)
gameWindow.AddObject(paddle)
// Ball
ballMaterial := globjects.NewMaterial()
ballMaterial.Color = mgl32.Vec4{1, 1, 1, 1}
ball := globjects.NewBox(1, 1, 1, mgl32.Vec3{-8, 0, 0}, ballMaterial)
ballBox := globjects.NewBox(1, 1, 1, mgl32.Vec3{-8, 0, 0}, ballMaterial)
ball := breakout.NewBall(ballBox)
ball.GLInit(gameWindow.GLProgram)
gameWindow.SetBall(ball)
gameWindow.AddObject(ball)
ball.Box.Velocity = mgl32.Vec3{.1, -.1, 0}
glSettings()
for gameWindow.IsRunning() {

80
pkg/breakout/ball.go Normal file
View File

@ -0,0 +1,80 @@
package breakout
import (
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/globjects"
"github.com/go-gl/mathgl/mgl32"
)
type Ball struct {
Box *globjects.Box
}
func NewBall(box *globjects.Box) *Ball {
ball := Ball{
Box: box,
}
return &ball
}
func (self *Ball) Update() {
self.Box.Update()
}
func (self *Ball) GLDraw() {
self.Box.GLDraw()
}
func (self *Ball) GLInit(glProgram uint32) {
self.Box.GLInit(glProgram)
}
func (self *Ball) ToggleWireframe() {
self.Box.ToggleWireframe()
}
func (self *Ball) GetAABB() *globjects.AABB {
return self.Box.GetAABB()
}
func (self *Ball) HandlePaddleCollision(paddle *Paddle) {
//intersects := self.Box.GetAABB().Intersects(paddle.GetAABB())
intersectsArray := self.Box.GetAABB().IntersectsArray(paddle.GetAABB())
if intersectsArray[1] {
// TODO special bounce based on where it hit
//self.Box.Velocity = self.Box.Velocity.Mul(-1)
self.Box.Velocity = mgl32.Vec3{0, 0, 0}
}
}
func (self *Ball) HandleTargetCollisions(targets *Targets) {
intersects := false
for _, brick := range targets.Bricks {
if !brick.Broken() {
intersects = self.Box.GetAABB().Intersects(brick.Box.GetAABB())
if intersects {
brick.Break()
// Bounce off of things
//self.Box.Velocity = self.Box.Velocity.Mul(-1)
self.Box.Velocity = mgl32.Vec3{0, 0, 0}
}
}
}
}
func (self *Ball) HandleBoxCollisions(objects []*globjects.Box) {
intersects := false
for _, object := range objects {
intersects = self.Box.GetAABB().Intersects(object.GetAABB())
if intersects {
// Bounce off of things
//self.Box.Velocity = self.Box.Velocity.Mul(-1)
self.Box.Velocity = mgl32.Vec3{0, 0, 0}
}
}
}

View File

@ -1,64 +0,0 @@
package breakout
import (
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/globjects"
"github.com/go-gl/mathgl/mgl32"
)
type BoundingBox struct {
Boxes []*globjects.Box
}
func NewBoundingBox(lowerLeft, topRight mgl32.Vec3, material *globjects.Material) *BoundingBox {
var depth float32 = 6.0
var height float32 = 4.0
var offset float32 = height / 2.0
boxes := make([]*globjects.Box, 4)
// Top
topPos := mgl32.Vec3{0, topRight.Y() + offset, topRight.Z()}
boxes[0] = globjects.NewBox(topRight.X()-lowerLeft.X()+height*2, height, depth, topPos, material)
// Bottom
bottomPos := mgl32.Vec3{0, lowerLeft.Y() - offset, lowerLeft.Z()}
boxes[1] = globjects.NewBox(topRight.X()-lowerLeft.X()+height*2, height, depth, bottomPos, material)
// Left
leftPos := mgl32.Vec3{lowerLeft.X() - offset, 0, lowerLeft.Z()}
boxes[2] = globjects.NewBox(height, topRight.Y()-lowerLeft.Y(), depth, leftPos, material)
// Right
rightPos := mgl32.Vec3{topRight.X() + offset, 0, topRight.Z()}
boxes[3] = globjects.NewBox(height, topRight.Y()-lowerLeft.Y(), depth, rightPos, material)
boundingBox := BoundingBox{
Boxes: boxes,
}
return &boundingBox
}
func (b *BoundingBox) GLInit(glProgram uint32) {
for _, box := range b.Boxes {
box.GLInit(glProgram)
}
}
func (b *BoundingBox) GLDraw() {
for _, box := range b.Boxes {
box.GLDraw()
}
}
func (b *BoundingBox) ToggleWireframe() {
for _, box := range b.Boxes {
box.ToggleWireframe()
}
}
func (b *BoundingBox) Update() {
for _, box := range b.Boxes {
box.Update()
}
}

View File

@ -49,3 +49,15 @@ func (b *Brick) SDLDraw() {
// TODO
}
}
func (b *Brick) GetAABB() *globjects.AABB {
return b.Box.GetAABB()
}
func (b *Brick) Broken() bool {
return b.broken
}
func (b *Brick) Break() {
b.broken = true
}

View File

@ -1,4 +1,4 @@
package gamewindow
package breakout
import (
"fmt"
@ -17,14 +17,16 @@ const defaultWindowFlags uint32 = sdl.WINDOW_SHOWN | sdl.WINDOW_RESIZABLE | sdl.
const fullscreenWindowFlags uint32 = defaultWindowFlags | sdl.WINDOW_FULLSCREEN_DESKTOP
type GameWindow struct {
SDLWindow *sdl.Window
GLContext *sdl.GLContext
GLProgram uint32
GLObjects []globjects.GLObject
Camera *globjects.Camera
Paddle *globjects.Box
PositiveBoundary mgl32.Vec3
NegativeBoundary mgl32.Vec3
SDLWindow *sdl.Window
GLContext *sdl.GLContext
GLProgram uint32
GLObjects []globjects.GLObject
Camera *globjects.Camera
Paddle *Paddle
Ball *Ball
Targets *Targets
SideWalls *SideWalls
AABB *globjects.AABB
keystates map[sdl.Keycode]bool
mouseMoved bool
@ -66,16 +68,22 @@ func NewGameWindow(title string) (*GameWindow, error) {
return &gameWindow, err
}
func (w *GameWindow) GLInit() error {
program, err := glhelpers.NewDefaultProgram()
func (w *GameWindow) GLInit(glProgram uint32) {
gl.UseProgram(glProgram)
w.GLProgram = glProgram
w.WindowProjection()
}
func (w *GameWindow) GLInitDefault() error {
glProgram, err := glhelpers.NewDefaultProgram()
if err != nil {
return err
}
gl.UseProgram(program)
w.GLProgram = program
w.GLInit(glProgram)
w.WindowProjection()
return nil
}
@ -121,10 +129,22 @@ func (w *GameWindow) WindowProjection() {
gl.Viewport(0, 0, width, height)
}
func (w *GameWindow) SetPaddle(paddle *globjects.Box) {
func (w *GameWindow) SetPaddle(paddle *Paddle) {
w.Paddle = paddle
}
func (w *GameWindow) SetBall(ball *Ball) {
w.Ball = ball
}
func (w *GameWindow) SetTargets(targets *Targets) {
w.Targets = targets
}
func (w *GameWindow) SetSideWalls(sidewalls *SideWalls) {
w.SideWalls = sidewalls
}
func (w *GameWindow) SetCamera(camera *globjects.Camera) {
w.Camera = camera
}
@ -144,11 +164,16 @@ func (w *GameWindow) ToggleWireframe() {
for _, o := range w.GLObjects {
o.ToggleWireframe()
}
w.Paddle.ToggleWireframe()
w.Ball.ToggleWireframe()
w.wireframe = !w.wireframe
}
func (w *GameWindow) GLDraw() {
model := mgl32.Ident4()
// Probably can't do concurrent drawing due to OpenGL
for _, o := range w.GLObjects {
glhelpers.SetUniformMatrix4f(w.GLProgram, "model", &model)
@ -177,13 +202,21 @@ func (w *GameWindow) Update() {
}
w.mouseMoved = false
for _, o := range w.GLObjects {
o.Update()
}
w.Camera.Update()
w.Ball.Update()
w.Ball.HandlePaddleCollision(w.Paddle)
w.Ball.HandleTargetCollisions(w.Targets)
w.Ball.HandleBoxCollisions(w.SideWalls.Boxes)
w.Paddle.Update()
w.ensurePaddleBoundary()
}
func (w *GameWindow) GetAABB() *globjects.AABB {
return w.AABB
}
func (w *GameWindow) HandleEvents() {
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch e := event.(type) {
@ -289,28 +322,30 @@ func (w *GameWindow) handleMouseMovementEvent(e *sdl.MouseMotionEvent) {
} else {
// Paddle movement
v := float32(e.XRel) / 50.0
w.Paddle.SetXVelocity(v)
w.Paddle.Box.SetXVelocity(v)
}
}
}
func (w *GameWindow) handleGameKeyboardMovement() {
if w.keystates[sdl.K_d] {
w.Paddle.SetXVelocity(0.5)
w.Paddle.Box.SetXVelocity(0.5)
} else if w.keystates[sdl.K_a] {
w.Paddle.SetXVelocity(-0.5)
w.Paddle.Box.SetXVelocity(-0.5)
} else {
w.Paddle.SetXVelocity(0)
w.Paddle.Box.SetXVelocity(0)
}
}
func (w *GameWindow) ensurePaddleBoundary() {
offset := w.Paddle.Width / 2.0
furthestLeft := w.NegativeBoundary.X() + offset
furthestRight := w.PositiveBoundary.X() - offset
if w.Paddle.Translation.X() < furthestLeft {
w.Paddle.Translation[0] = furthestLeft
} else if w.Paddle.Translation.X() > furthestRight {
w.Paddle.Translation[0] = furthestRight
b := w.Paddle.Box
offset := b.Width / 2.0
furthestLeft := w.GetAABB().BottomLeft.X() + offset
furthestRight := w.GetAABB().TopRight.X() - offset
if b.Translation.X() < furthestLeft {
w.Paddle.Box.Translation[0] = furthestLeft
} else if b.Translation.X() > furthestRight {
w.Paddle.Box.Translation[0] = furthestRight
}
}

35
pkg/breakout/paddle.go Normal file
View File

@ -0,0 +1,35 @@
package breakout
import "gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/globjects"
type Paddle struct {
Box *globjects.Box
}
func (self *Paddle) Update() {
self.Box.Update()
}
func (self *Paddle) GLDraw() {
self.Box.GLDraw()
}
func (self *Paddle) GLInit(glProgram uint32) {
self.Box.GLInit(glProgram)
}
func (self *Paddle) ToggleWireframe() {
self.Box.ToggleWireframe()
}
func (self *Paddle) GetAABB() *globjects.AABB {
return self.Box.GetAABB()
}
func NewPaddle(box *globjects.Box) *Paddle {
paddle := Paddle{
Box: box,
}
return &paddle
}

49
pkg/breakout/sidewalls.go Normal file
View File

@ -0,0 +1,49 @@
package breakout
import (
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/globjects"
"github.com/go-gl/mathgl/mgl32"
)
type SideWalls struct {
Boxes []*globjects.Box
}
func NewSideWalls(aabb *globjects.AABB, material *globjects.Material) *SideWalls {
topRight := aabb.TopRight
bottomLeft := aabb.BottomLeft
var depth float32 = 6.0
var height float32 = 4.0
var offset float32 = height / 2.0
boxes := make([]*globjects.Box, 4)
// Top
topPos := mgl32.Vec3{0, topRight.Y() + offset, topRight.Z()}
boxes[0] = globjects.NewBox(topRight.X()-bottomLeft.X()+height*2, height, depth, topPos, material)
// Bottom
bottomPos := mgl32.Vec3{0, bottomLeft.Y() - offset, bottomLeft.Z()}
boxes[1] = globjects.NewBox(topRight.X()-bottomLeft.X()+height*2, height, depth, bottomPos, material)
// Left
leftPos := mgl32.Vec3{bottomLeft.X() - offset, 0, bottomLeft.Z()}
boxes[2] = globjects.NewBox(height, topRight.Y()-bottomLeft.Y(), depth, leftPos, material)
// Right
rightPos := mgl32.Vec3{topRight.X() + offset, 0, topRight.Z()}
boxes[3] = globjects.NewBox(height, topRight.Y()-bottomLeft.Y(), depth, rightPos, material)
sideWalls := SideWalls{
Boxes: boxes,
}
return &sideWalls
}
func (self *SideWalls) GLInit(glProgram uint32) {
for _, box := range self.Boxes {
box.GLInit(glProgram)
}
}

View File

@ -6,11 +6,11 @@ import (
)
type Targets struct {
Rows int
Columns int
Bricks []*Brick
TopRight mgl32.Vec3
LowerLeft mgl32.Vec3
Rows int
Columns int
Bricks []*Brick
TopRight mgl32.Vec3
BottomLeft mgl32.Vec3
}
func NewTargets(numRows, numColumns int, adjustPos mgl32.Vec3) *Targets {
@ -30,12 +30,12 @@ func NewTargets(numRows, numColumns int, adjustPos mgl32.Vec3) *Targets {
bw := brickWidth + widthOffset
bh := brickHeight + heightOffset
lowerLeftPos := mgl32.Vec3{
bottomLeftPos := mgl32.Vec3{
bw/2 - float32(numColumns)*(bw)/2,
bh/2 - float32(numRows)*(bh)/2,
0,
}
lowerLeftPos = lowerLeftPos.Add(adjustPos)
bottomLeftPos = bottomLeftPos.Add(adjustPos)
topRightPos := mgl32.Vec3{
bw/2 + float32(numColumns)*(bw)/2,
@ -45,7 +45,7 @@ func NewTargets(numRows, numColumns int, adjustPos mgl32.Vec3) *Targets {
topRightPos = topRightPos.Add(adjustPos)
targets.TopRight = topRightPos
targets.LowerLeft = lowerLeftPos
targets.BottomLeft = bottomLeftPos
materials := make([]*globjects.Material, numColumns)
@ -60,7 +60,7 @@ func NewTargets(numRows, numColumns int, adjustPos mgl32.Vec3) *Targets {
n := 0
for i := 0; i < numRows; i++ {
for j := 0; j < numColumns; j++ {
position := lowerLeftPos.Add(mgl32.Vec3{
position := bottomLeftPos.Add(mgl32.Vec3{
float32(j) * bw,
float32(i) * bh,
0})

5
pkg/globjects/README.md Normal file
View File

@ -0,0 +1,5 @@
# globjects
This package contains things that can be drawn with OpenGL along with
any helpers. Effectively, anything in here should implement the
GLObject interface.

61
pkg/globjects/aabb.go Normal file
View File

@ -0,0 +1,61 @@
package globjects
import (
"github.com/go-gl/mathgl/mgl32"
)
// AABB implements a simple Axis-Aligned Bounding Box for collision detection.
type AABB struct {
TopRight mgl32.Vec3
BottomLeft mgl32.Vec3
}
func NewAABB(topRight, bottomLeft mgl32.Vec3) *AABB {
aabb := AABB{
TopRight: topRight,
BottomLeft: bottomLeft,
}
return &aabb
}
func NewAABBFromBox(box *Box) *AABB {
pos := box.Translation
xOffset := box.Width / 2.0
yOffset := box.Height / 2.0
zOffset := box.Depth / 2.0
offset := mgl32.Vec3{
xOffset,
yOffset,
zOffset,
}
topLeft := pos.Add(offset)
bottomRight := pos.Sub(offset)
return NewAABB(topLeft, bottomRight)
}
func (self *AABB) Intersects(other *AABB) bool {
boolArray := self.IntersectsArray(other)
intersects := boolArray[0] && boolArray[1] && boolArray[2]
return intersects
}
func (self *AABB) IntersectsArray(other *AABB) []bool {
boolArray := make([]bool, 3)
if other == nil {
return boolArray
}
boolArray[0] = self.BottomLeft.X() <= other.TopRight.X() && self.TopRight.X() >= other.BottomLeft.X()
boolArray[1] = self.BottomLeft.Y() <= other.TopRight.Y() && self.TopRight.Y() >= other.BottomLeft.Y()
boolArray[2] = self.BottomLeft.Z() <= other.TopRight.Z() && self.TopRight.Z() >= other.BottomLeft.Z()
return boolArray
}

View File

@ -23,6 +23,7 @@ type Box struct {
GLVertexArrayID uint32
GLVertexBufferID uint32
Wireframe bool
AABB *AABB
}
func NewBox(
@ -70,6 +71,7 @@ func NewBox(
}
box.VertexArray = box.GetVertexArray()
box.AABB = NewAABBFromBox(&box)
return &box
}
@ -192,3 +194,7 @@ func (b *Box) SetZVelocity(v float32) {
func (b *Box) SetYRotationVelocity(v float32) {
b.RotationVelocity[1] = v
}
func (b *Box) GetAABB() *AABB {
return b.AABB
}

View File

@ -51,6 +51,10 @@ func (c *Camera) GLDraw() {
glhelpers.SetUniformVec3f(c.GLProgram, "cameraPos", c.Position)
}
func (c *Camera) GLInit(glProgram uint32) {
return
}
func (c *Camera) Update() {
c.Rotate(0, 0, c.RotationVelocity)
c.Move(c.Velocity)
@ -60,6 +64,10 @@ func (c *Camera) ToggleWireframe() {
return
}
func (c *Camera) GetAABB() *AABB {
return nil
}
func (c *Camera) Rotate(mouseX, mouseY int32, velocity float32) {
angle := mgl32.Vec3{}
// X Rotation (pitch) - mouse up/down

View File

@ -3,5 +3,7 @@ package globjects
type GLObject interface {
Update()
GLDraw()
GLInit(glProgram uint32)
ToggleWireframe()
GetAABB() *AABB
}