func (self *TopDownTestScene) SwapInput(event events.Event) { log.Println("Swap Input!") if !event.Pressed { return } if self.inputPlayer { self.playerCube.RemoveComponent(components.INPUT) components.GetTransform(self.playerCube).Halt() self.game.Camera.AddComponent(FixedCameraInput) self.inputPlayer = false self.topDownCamera.PauseTracking() log.Println("[Camera] Player now", self.playerCube) } else { self.game.Camera.RemoveComponent(components.INPUT) components.GetTransform(self.game.Camera.Entity).Halt() self.playerCube.AddComponent(FixedYInput) self.inputPlayer = true self.topDownCamera.ResumeTracking() log.Println("[Player] Player now", self.playerCube) } }
func (self *TexturedCube) Setup() { self.theCube = core.NewEntity() self.theCube.Name = "Textured Cube" self.theCube.AddComponent(new(components.Visual)) self.colorCube = core.NewEntity() self.colorCube.Name = "Colored Cube" self.colorCube.AddComponent(&components.Visual{ MaterialName: "only_color", MeshName: "ColorIndexCube", }) transform := components.GetTransform(self.theCube) transform.Position = math3d.Vector{0, 0, -10} transform.Scale = math3d.Vector{2, 2, 2} transform.Speed = math3d.Vector{5, 5, 5} transform = components.GetTransform(self.colorCube) transform.Position = math3d.Vector{0, 0, -5} // transform.MoveRelativeToRotation = true // self.theCube.AddComponent(&components.Input{ // Mapping: FPSMapping, // }) skybox := factories.SkyBox("stevecube", self.game.Camera) self.game.RegisterEntity(skybox) self.game.RegisterEntity(self.theCube) self.game.RegisterEntity(self.colorCube) }
func (self *TopDownTestScene) Setup() { self.game.RegisterEntity(factories.SkyBox("stevecube", self.game.Camera)) self.levelVolume = &volume.FunctionVolume{ func(x, y, z float32) float32 { if y > 5 && (x > 3 && x < 47) && (z > 3 && z < 47) { return -1 } else { return 1 } }, } volumeMesh := volume.MarchingCubes(self.levelVolume, math3d.Vector{50, 10, 50}, 0.5) volumeMesh.Name = "Level Mesh" self.levelEntity = core.NewEntity() self.levelEntity.Name = "Level Geometry" self.levelEntity.AddComponent(&components.Visual{ Mesh: volumeMesh, MaterialName: "only_color", }) self.game.RegisterEntity(self.levelEntity) self.game.Camera.AddComponent(&components.Input{ Mapping: FPSMapping, }) // Get the camera facing downwards cameraTransform := components.GetTransform(self.game.Camera.Entity) cameraTransform.Position = math3d.Vector{25, 10, 25} cameraTransform.CurrentPitch = 90 cameraTransform.Speed = math3d.Vector{8, 8, 8} cameraTransform.MoveRelativeToRotation = false // Our unit we'll control self.playerCube = core.NewEntityAt(math3d.Vector{25, 6, 25}) self.playerCube.Name = "The Player" self.playerCube.AddComponent(&components.Visual{}) playerTransform := components.GetTransform(self.playerCube) playerTransform.Scale = math3d.Vector{0.25, 0.5, 0.25} playerTransform.Speed = math3d.Vector{3, 3, 3} playerTransform.MoveRelativeToRotation = false self.topDownCamera = NewTopDownCamera(self.game.Camera) self.topDownCamera.SetTrackingHeight(5) self.topDownCamera.TrackEntity(self.playerCube) self.game.RegisterEntity(self.playerCube) self.game.Keyboard.OnKey(input.KeySpace, func(event events.Event) { self.SwapInput(event) }) // Start by controlling the player unit. Game defaults to controlling the camera self.SwapInput(events.Event{Pressed: true}) }
func (self *Transform) Update(deltaT float32) { var transform *components.Transform for _, entity := range self.entitySet.Entities() { transform = components.GetTransform(entity) if transform.UsingPositionOf == nil { self.processMovement(deltaT, transform) self.processRotation(deltaT, transform) } else { transform.Position = components.GetTransform(transform.UsingPositionOf).Position } } }
func (self *Level) Generate() *core.Entity { // A square arena with walls self.volume = &volume.FunctionVolume{ func(x, y, z float32) float32 { if y > 5 && (x > 3 && x < 47) && (z > 3 && z < 47) { return -1 } else { return 1 } }, } volumeMesh := volume.MarchingCubes(self.volume, math3d.Vector{50, 10, 50}, 0.5) volumeMesh.Name = "Level Mesh" self.entity = core.NewEntity() self.entity.Name = "Level Geometry" self.entity.AddComponent(&components.Visual{ Mesh: volumeMesh, MaterialName: "only_color", }) transform := components.GetTransform(self.entity) transform.Position = math3d.Vector{-25, -10, -25} return self.entity }
// Make the entity always look towards the direction of the mouse cursor // Always rotate around +Y (yaw) func fixedYMouseMoved(entity components.ComponentHolder, event events.Event) { transform := components.GetTransform(entity) transform.CurrentYaw = math3d.RadToDeg( math3d.Atan2(float32(event.MouseYDiff), float32(event.MouseXDiff)), )*-1 + 90 }
func (self *Player) initializeComponents() { topDownInput := &components.Input{ Mapping: components.InputEventMap{ events.MoveForward: self.moveForward, events.MoveBackward: self.moveBackward, events.MoveLeft: self.moveLeft, events.MoveRight: self.moveRight, events.MouseMove: self.faceMouse, }, Polling: []events.EventType{ events.MoveForward, events.MoveBackward, events.MoveLeft, events.MoveRight, }, } self.entity.AddComponent(&components.Visual{}) self.entity.AddComponent(topDownInput) transform := components.GetTransform(self.entity) transform.Scale = math3d.Vector{0.25, 0.5, 0.25} transform.Speed = math3d.Vector{3, 3, 3} transform.MoveRelativeToRotation = false }
func (self *Player) faceMouse(_ components.ComponentHolder, event events.Event) { transform := components.GetTransform(self.entity) transform.CurrentYaw = math3d.RadToDeg( math3d.Atan2(float32(event.MouseYDiff), float32(event.MouseXDiff)), )*-1 + 90 }
// NewEntityAt sets up a new Entity for use in the app and sets the // initial Transform component's Position to the given Vector func NewEntityAt(startingPosition math3d.Vector) *Entity { entity := NewEntity() transform := components.GetTransform(entity) transform.Position = startingPosition return entity }
func Test_Update_CopiesPositionFromLinkedEntity(t *testing.T) { behavior, entityDb := getTestTransform() entity := core.NewEntity() entityDb.RegisterEntity(entity) followee := core.NewEntity() followeeTransform := components.GetTransform(followee) followeeTransform.Position = math3d.Vector{10, 11, -12} transform := components.GetTransform(entity) transform.UsingPositionOf = followee behavior.Update(1) assert.Equal(t, math3d.Vector{10, 11, -12}, transform.Position) }
// UpdatePosition calculates where this camera needs to be to stay tracking // on the Entity, handling any potential changes of the Entity's position func (self *TopDownCamera) UpdatePosition() { if self.currentlyTracking { entityPosition := components.GetTransform(self.trackingEntity).Position entityPosition.Y += self.trackingHeight self.camera.SetPosition(entityPosition) } }
// ResumeTracking moves the camera back to the Entity being tracked and resumes // camera position updates func (self *TopDownCamera) ResumeTracking() { aboveEntity := components.GetTransform(self.trackingEntity).Position aboveEntity.Y += self.trackingHeight self.camera.AddComponent( components.NewPositionAnimation( aboveEntity, 0.5, func() { self.currentlyTracking = true }, ), ) }
func NewCamera() *Camera { camera := new(Camera) camera.Entity = NewEntity() camera.Entity.Name = "Camera" camera.transform = components.GetTransform(camera.Entity) camera.transform.MoveRelativeToRotation = true return camera }
func (self *VolumeScene) Setup() { skybox := factories.SkyBox("stevecube", self.game.Camera) self.game.RegisterEntity(skybox) self.game.Camera.AddComponent(FPSInput) self.game.Camera.SetSpeed(math3d.Vector{5, 5, 5}) self.game.Camera.LookAt(math3d.Vector{0, 0, -5}) self.game.Keyboard.OnKey(input.KeyJ, func(e events.Event) { if e.Pressed { self.marchingCubeSize -= 0.1 if self.marchingCubeSize <= 0 { self.marchingCubeSize = 0.1 } else { self.rebuildVolume() } } }) self.game.Keyboard.OnKey(input.KeyK, func(e events.Event) { if e.Pressed { self.marchingCubeSize += 0.1 self.rebuildVolume() } }) self.cubeVolume = &volume.FunctionVolume{ // A sphere! func(x, y, z float32) float32 { // Translate to treat middle of the volume as 0,0,0 // Basically I want 0,0,0 of the volume to act like -25, -25, -25, so that // the center point of the sphere is at 25, 25, 25 in the volume, // then check the equation against the radius of the sphere. tX := x - 25 tY := y - 25 tZ := z - 25 return -(tX*tX + tY*tY + tZ*tZ - 20) }, } self.volumeEntity = core.NewEntity() self.volumeEntity.Name = "cube volume" self.rebuildVolume() // Move the volume into view of the starting camera transform := components.GetTransform(self.volumeEntity) transform.Position = math3d.Vector{-25, -25, -40} self.game.RegisterEntity(self.volumeEntity) }
func Test_SkyBox(t *testing.T) { camera := core.NewCamera() skybox := SkyBox("testMap", camera) visual := components.GetVisual(skybox) transform := components.GetTransform(skybox) assert.Equal(t, "Skybox testMap", skybox.Name) assert.Equal(t, "testMap", visual.MaterialName) assert.Equal(t, camera.Entity, transform.UsingPositionOf) }
func Test_Update_UpdatesAPositionAnimationOverTime(t *testing.T) { behavior, entityDb := getTestAnimation() entity := core.NewEntity() entity.AddComponent( components.NewPositionAnimation(math3d.Vector{2, 2, 2}, 1, func() {}), ) entityDb.RegisterEntity(entity) // Move half way behavior.Update(0.5) transform := components.GetTransform(entity) assert.Equal(t, math3d.Vector{1, 1, 1}, transform.Position) // Move the rest of the way behavior.Update(0.5) transform = components.GetTransform(entity) assert.Equal(t, math3d.Vector{2, 2, 2}, transform.Position) }
func Test_Update_AppliesEulerAngles(t *testing.T) { behavior, entityDb := getTestTransform() entity := core.NewEntity() entityDb.RegisterEntity(entity) transform := components.GetTransform(entity) startingQuat := transform.Rotation transform.CurrentPitch = 45 behavior.Update(1) assert.NotEqual(t, startingQuat, transform.Rotation) }
func Test_MouseMove(t *testing.T) { event := events.Event{ MouseXDiff: 30, MouseYDiff: 40, } entity := core.NewEntity() transform := components.GetTransform(entity) FPSMapping[events.MouseMove](entity, event) assert.Equal(t, 15, transform.CurrentYaw) assert.Equal(t, 20, transform.CurrentPitch) }
func Test_MouseMove_ConstrainsYaw(t *testing.T) { event := events.Event{ MouseXDiff: 40, MouseYDiff: 0, } entity := core.NewEntity() transform := components.GetTransform(entity) transform.CurrentYaw = 350 FPSMapping[events.MouseMove](entity, event) assert.Equal(t, 10, transform.CurrentYaw) }
func Test_Update_AppliesSpeedToMovingDir(t *testing.T) { behavior, entityDb := getTestTransform() entity := core.NewEntity() entityDb.RegisterEntity(entity) transform := components.GetTransform(entity) transform.Moving(math3d.Vector{1, 0, 0}) transform.Speed = math3d.Vector{2, 2, 2} // Time passed? change! behavior.Update(0.5) assert.Equal(t, math3d.Vector{1, 0, 0}, transform.Position) }
func Test_MouseMove_ClampsPitch(t *testing.T) { event := events.Event{ MouseXDiff: 0, MouseYDiff: 40, } entity := core.NewEntity() transform := components.GetTransform(entity) transform.CurrentPitch = 80 FPSMapping[events.MouseMove](entity, event) assert.Equal(t, 89, transform.CurrentPitch) }
// SkyBox returns an Entity that is set up as a SkyBox. The material given // needs to be the name of a cube-map Material definition. The resulting entity // will be position-locked to the passed in camera so that it never looks like // it's moving. func SkyBox(skyboxMaterial string, camera *core.Camera) *core.Entity { entity := core.NewEntity() entity.Name = "Skybox " + skyboxMaterial visual := new(components.Visual) visual.MeshName = "SkyBoxMesh" visual.MaterialName = skyboxMaterial transform := components.GetTransform(entity) transform.UsingPositionOf = camera.Entity transform.Scale = math3d.Vector{20, 20, 20} entity.AddComponent(visual) return entity }
func Test_Update_MovesWithRotationIfSoFlagged(t *testing.T) { behavior, entityDb := getTestTransform() entity := core.NewEntity() entityDb.RegisterEntity(entity) transform := components.GetTransform(entity) transform.Moving(math3d.Vector{1, 0, 0}) transform.Speed = math3d.Vector{1, 1, 1} transform.Rotation = math3d.Quaternion{0, 0, 1, 0} transform.MoveRelativeToRotation = true // Time passed? change! behavior.Update(1) assert.Equal(t, math3d.Vector{-1, 0, 0}, transform.Position) }
func Test_Update_UpdatesOverMultipleSeconds(t *testing.T) { behavior, entityDb := getTestAnimation() entity := core.NewEntity() entity.AddComponent( components.NewPositionAnimation(math3d.Vector{10, 10, 10}, 10, func() {}), ) entityDb.RegisterEntity(entity) var i float32 = 1 for ; i <= 10; i++ { behavior.Update(1) transform := components.GetTransform(entity) assert.Equal(t, math3d.Vector{i, i, i}, transform.Position) } }
func Test_Update_AppliesMovementDirOnTransforms(t *testing.T) { behavior, entityDb := getTestTransform() entity := core.NewEntity() entityDb.RegisterEntity(entity) transform := components.GetTransform(entity) transform.Moving(math3d.Vector{1, 0, 0}) // No time passed? no change behavior.Update(0) assert.Equal(t, math3d.Vector{0, 0, 0}, transform.Position) // Time passed? change! behavior.Update(1) assert.Equal(t, math3d.Vector{1, 0, 0}, transform.Position) }
func Test_Update_AppliesRotationDir(t *testing.T) { behavior, entityDb := getTestTransform() entity := core.NewEntity() entityDb.RegisterEntity(entity) transform := components.GetTransform(entity) transform.Rotating(math3d.Vector{0, 1, 0}) startingQuat := transform.Rotation // No time passed? no change behavior.Update(0) assert.Equal(t, startingQuat, transform.Rotation) // Time passed? change! behavior.Update(1) assert.NotEqual(t, startingQuat, transform.Rotation) }
func Test_FixedCamera_CardinalMovement(t *testing.T) { event := events.Event{} for _, testValue := range panningCardinalTests { entity := core.NewEntity() transform := components.GetTransform(entity) // Set on pressed event.Pressed = true FixedCameraMapping[testValue.event](entity, event) assert.Equal(t, testValue.expectedDir, transform.MoveDir()) // And undo the change on key release event.Pressed = false FixedCameraMapping[testValue.event](entity, event) assert.Equal(t, math3d.Vector{0, 0, 0}, transform.MoveDir()) } }
func Test_Turning(t *testing.T) { event := events.Event{} for _, testValue := range turnTests { entity := core.NewEntity() transform := components.GetTransform(entity) // Set on pressed event.Pressed = true FPSMapping[testValue.event](entity, event) assert.Equal(t, testValue.expectedDir, transform.RotateDir()) // And undo the change on key release event.Pressed = false FPSMapping[testValue.event](entity, event) assert.Equal(t, math3d.Vector{0, 0, 0}, transform.RotateDir()) } }
// Simple linear animation between two positions over time func animatePosition(deltaT float32, entity *core.Entity) bool { animation := components.GetAnimation(entity) transform := components.GetTransform(entity) // Move more of this into the Animation? // Basically we are building this to figure out the distance we need to travel // in the current tick to have consistent, linear movement over the time period requested. // Probably better ways of calculating this. if animation.CurrentTweenTick == 0 { animation.TweenStartAt = transform.Position } animation.CurrentTweenTick += deltaT tweenPercent := deltaT / animation.TweenTime movePerFrame := animation.TweenTo.Sub(animation.TweenStartAt).Scale(tweenPercent) transform.Position = transform.Position.Add(movePerFrame) return animation.CurrentTweenTick >= animation.TweenTime }
// Update is called every Game tick func (self *Graphical) Update(camera *core.Camera, deltaT float32) { self.renderer.BeginRender() var visual *components.Visual renderQueue := render.NewRenderQueue() renderQueue.ProjectionMatrix = camera.ProjectionMatrix() renderQueue.ViewMatrix = camera.ViewMatrix() for _, entity := range self.entitySet.Entities() { visual = components.GetVisual(entity) renderQueue.Add(render.RenderOperation{ Mesh: self.meshFromVisual(visual), Material: self.materialLoader.Get(visual.MaterialName), Transform: components.GetTransform(entity).TransformMatrix(), }) } self.renderer.Render(renderQueue) self.renderer.FinishRender() }