Beispiel #1
0
// CreateGoalEntity creates the game's goal entity and places
// it at a good spot in the world.
func (s *Server) createGoalEntity() {
	// calculate a center location
	centerX := math.Floor(float64((float32(s.levelWidth)/2.0)*float32(landscape.ChunkSize))) + 0.5
	centerZ := math.Floor(float64((float32(s.levelHeight)/2.0)*float32(landscape.ChunkSize))) + 0.5
	tallest, err := s.landscapeMan.GetHeightAt(int(centerX), int(centerZ))
	if err != nil {
		groggy.Logsf("ERROR", "S: CreateGoalLocation() couldn't figure out the height at X,Z: %f,%f. %v", centerX, centerZ, err)
		return
	}

	// create the entity
	groggy.Logsf("DEBUG", "S: CreateGoalLocation() will create goal at %f,%f,%f for height of %d.", centerX, float32(tallest)+1.5, centerZ, tallest)
	playerGoal := s.entityManager.RegisterNewEntity()
	playerGoal.Name = "PlayerGoal"
	playerGoal.AssetPath = "goal"
	playerGoal.Location = mgl.Vec3{float32(centerX), float32(tallest) + 1.5, float32(centerZ)}
	playerGoal.Collider = physics.NewCollisionCube(nil, physmath.Vector3{0.5, 0.5, 0.5})
	goalBody := playerGoal.Collider.GetBody()
	goalBody.SetInfiniteMass()
	goalBody.Acceleration = physmath.Vector3{0.0, 0.0, 0.0}
	playerGoal.SyncToColliders()
	goalBody.CalculateDerivedData()
	playerGoal.Collider.CalculateDerivedData()

	// set the goal for the player
	s.player.Goal = playerGoal
}
Beispiel #2
0
// CreateLocalPlayerForServer makes an object to pass to the server
// in a local connection so that the server can directly trigger
// the callbacks.
func (gm *LocalGameManager) CreateLocalPlayerForServer() *server.LocalPlayer {
	// Make the object itself for the closure. This tactic subverts the problem
	// of circular references between gamemanager <--> server.
	localPlayer := server.NewLocalPlayer()

	localPlayer.OnEntityForcedReceived = func(e *entity.Entity) {
		groggy.Logsf("DEBUG", "LP: OnEntityForcedReceived: Entity name: %s ; Id: %d", e.Name, e.Id)
		gm.entityManager.ForceUpdate(e)
	}

	// OnPlayerConnected should be called when the player finishes connecting to the server.
	localPlayer.OnPlayerConnected = func(e *entity.Entity) {
		groggy.Logsf("DEBUG", "LP: OnPlayerConnected: Player name: %s ; Id: %d", e.Name, e.Id)
		if e.Collider != nil {
			groggy.Logsf("DEBUG", "LP: OnPlayerConnected: Player collider position: %v", e.Collider.GetBody().Position)
		}
		// set the playerEntity in the game manager for future use
		gm.playerEntityId = e.Id

		// store the player entity
		gm.entityManager.ForceUpdate(e)

		// now that we've connected, request the game landscape
		gm.RequestLandscapeReload()
	}

	localPlayer.OnLandscapeChunkUpdate = func(c *landscape.Chunk) {
		groggy.Logsf("DEBUG", "LP: OnLandscapeChunkUpdate: Location: %d, %d, %d", c.X, c.Y, c.Z)
		gm.landscapeMan.RegisterChunk(c)

		// update the physics collisions client-side
		c.UpdateColliders()

		// pull a render node just to build it in memory
		_ = c.GetTheRenderable()
	}

	localPlayer.OnStartGameplay = func() {
		groggy.Logsf("DEBUG", "LP: OnStartGameplay")

		// the game has started so enable phyics
		gm.physicsEnabled = true
		gm.physicsLastTime = time.Now()
	}

	return localPlayer
}
Beispiel #3
0
// Destroy tells the chunk to release any special data.
func (c *Chunk) Destroy() {
	groggy.Logsf("DEBUG", "Chunk.Destroy() @ %d,%d,%d", c.X, c.Y, c.Z)

	// destroy the renderable if there's one made for the landscape node
	if c.Renderable != nil {
		c.Renderable.Destroy()
	}
}
Beispiel #4
0
// RegisterEntity attempts to register the entity passed in. If an entity
// is already found with that Id, the function does nothing and returns false.
// Otherwise it updates the entity map and returns true.
func (m *Manager) RegisterEntity(e *Entity) bool {
	_, okay := m.entities[e.Id]
	if okay == true {
		groggy.Logsf("DEBUG", "EntityManager.RegisterEntity() attempted to register a duplicate entity (id=%d)", e.Id)
		return false
	}

	m.entities[e.Id] = e
	return true
}
Beispiel #5
0
// setupTestAssets will load up the test prototypes in the assets manager
func setupTestAssets() {
	// setup a test cube for the goal
	goalCube := fizzle.CreateCube("diffuse", -0.5, -0.5, -0.5, 0.5, 0.5, 0.5)
	goalCube.Core.Shader = gameRenderer.Shaders["diffuse"]
	goalCube.Core.DiffuseColor = mgl.Vec4{0.95, 0.1, 0.1, 1.0}
	goalCube.Core.SpecularColor = mgl.Vec4{1.0, 0.0, 0.0, 1.0}
	goalCube.Core.Shininess = 4.8
	loadedAssets["goal"] = goalCube
	groggy.Logsf("INFO", "Asset loaded: goal (shader %d)", goalCube.Core.Shader.Prog)

	// setup a test cube for the player
	playerCube := fizzle.CreateCube("diffuse", -0.45, 0.0, -0.45, 0.45, 1.5, 0.45)
	playerCube.Core.Shader = gameRenderer.Shaders["diffuse"]
	playerCube.Core.DiffuseColor = mgl.Vec4{0.1, 0.1, 0.95, 1.0}
	playerCube.Core.SpecularColor = mgl.Vec4{0.0, 0.0, 1.0, 1.0}
	playerCube.Core.Shininess = 0.8
	loadedAssets["player"] = playerCube
	groggy.Log("INFO", "Asset loaded: player")

}
Beispiel #6
0
// fireDebugPrint prints out some information that might be useful to the terminal
func fireDebugPrint(delta float32) {
	playerEnt := gameManager.GetLocalPlayer()
	body := playerEnt.Collider.GetBody()
	groggy.Logsf("DEBUG", "Player location: %v (collider position: %v)", playerEnt.Location, body.Position)
}
Beispiel #7
0
// UpdatePhysics makes sure the physics objects are updated.
func (gm *LocalGameManager) UpdatePhysics(frameDelta float32) {
	if gm.physicsEnabled == false {
		return
	}

	physicsNow := time.Now()
	physicsDelta := float32(physicsNow.Sub(gm.physicsLastTime).Seconds())

	// sync physics to no more than 60 fps
	if physicsDelta < 1.0/60.0 {
		return
	}

	// conversely, if it's been too long, we just act like nothing happened ...
	// what's up with that?
	if physicsDelta > 0.50 {
		// try again next time to see if we can process the world
		gm.physicsLastTime = physicsNow
		groggy.Logsf("INFO", "LGM:UpdatePhycis() had to skip a physics frame due to excessive lag in delta time (%f)", physicsDelta)
		return
	}

	// update the collider in each entity
	gm.entityManager.Map(func(e *entity.Entity) {
		if e.Collider != nil {
			e.RunCollider(physicsDelta)
		}
	})

	// generate any contacts
	//groggy.Logsf("DEBUG", "LGM: UpdatePhysics: checking start")
	var contacts []*physics.Contact
	gm.entityManager.Map(func(e *entity.Entity) {
		if e.Collider != nil {
			// test collisions against other entities
			// TODO: do better coarse collision detection
			gm.entityManager.Map(func(otherEnt *entity.Entity) {
				if e != otherEnt && otherEnt.Collider != nil {
					//groggy.Logsf("DEBUG", "\tLGM: UpdatePhysics: checking entitys:")
					//groggy.Logsf("DEBUG", "\t\tentity %d(%s) @ (%v)", e.Id, e.Name, e.Collider.GetTransform())
					//groggy.Logsf("DEBUG", "\t\t%v", e.Collider.GetBody())
					//groggy.Logsf("DEBUG", "\t\tentity %d(%s) @ (%v)", otherEnt.Id, otherEnt.Name, otherEnt.Collider.GetTransform())
					//groggy.Logsf("DEBUG", "\t\t%v", otherEnt.Collider.GetBody())
					_, contacts = physics.CheckForCollisions(e.Collider, otherEnt.Collider, contacts)
				}
			})

			// stop here with the infinite mass, non-movable entities
			if e.Collider.GetBody().HasFiniteMass() == false {
				return
			}

			// Now check the entity against the landscape chunks
			// TODO: super lame selections of chunks and very wasteful
			intX, intZ := int(e.Location[0]), int(e.Location[2])
			chunks := gm.landscapeMan.GetChunksFor(intX, intZ)
			chunks = append(chunks, gm.landscapeMan.GetChunksFor(intX, intZ+1)...)
			chunks = append(chunks, gm.landscapeMan.GetChunksFor(intX, intZ-1)...)
			chunks = append(chunks, gm.landscapeMan.GetChunksFor(intX+1, intZ-1)...)
			chunks = append(chunks, gm.landscapeMan.GetChunksFor(intX-1, intZ-1)...)
			for _, chunk := range chunks {
				// check collision against the relevant landscape collisions
				for _, chunkCollider := range chunk.Colliders {
					_, contacts = physics.CheckForCollisions(e.Collider, chunkCollider, contacts)
				}
			}
		}
	})

	// resolve the contacts
	if contacts != nil && len(contacts) > 0 {
		physics.ResolveContacts(len(contacts)*2, contacts, physmath.Real(physicsDelta))
	}

	gm.physicsLastTime = physicsNow
}
Beispiel #8
0
// main is the main entry point for the client.
func main() {
	// Setup the log handlers
	groggy.Register("DEBUG", nil)
	groggy.Register("INFO", nil)
	groggy.Register("ERROR", nil)

	// for now, create a default options structure
	loadedOptions = NewOptions()

	// create a window for the game
	var err error
	mainWindow, err = createWindow(loadedOptions)
	if err != nil {
		groggy.Logsf("ERROR", "Failed to create a window for the game:\n%v", err)
		os.Exit(1)
	}

	// setup the input callbacks on the window
	setupCallbacks(mainWindow)
	setupInputModels(mainWindow)

	// setup the rendering engine
	gameRenderer, err = createRenderer(mainWindow)
	if err != nil {
		groggy.Logsf("ERROR", "Failed to create the game renderer:\n%v", err)
		os.Exit(1)
	}

	// load the assets
	loadedAssets = make(map[string]*fizzle.Renderable)
	setupTestAssets()

	// setup a new game manager
	// NOTE: for now we hard code it to the local game manager
	gameManager = gamemanager.NewLocalGameManager()

	// start the local game on the server
	gameManager.StartLocalGame(loadedOptions.PlayerName)

	// main game loop
	lastFrame := time.Now()
	for !mainWindow.ShouldClose() {
		// calculate the difference in time
		thisFrame := time.Now()
		frameDelta := float32(thisFrame.Sub(lastFrame).Seconds())

		// do time-sensitive inputs
		inputUpdate(mainWindow, frameDelta)

		// update physics
		gameManager.UpdatePhysics(frameDelta)

		// HACK: update the camera if there's enough change
		player := gameManager.GetLocalPlayer()
		camTarget := gameRenderer.camera.GetTarget()
		deltaCam := camTarget.Sub(player.Location)
		if deltaCam.Dot(deltaCam) > 0.001 {
			gameRenderer.camera.SetTarget(player.Location)
		}

		// draw the frame
		gameRenderer.DoRender(frameDelta)

		// update our last frame time
		lastFrame = thisFrame
	}
}
Beispiel #9
0
// DoRender does the graphical rendering of a single frame.
func (gr *GameRenderer) DoRender(delta float32) {
	// detect any changes to size of the window
	tempW, tempH := gr.MainWindow.GetFramebufferSize()
	currentWidth, currentHeight := int32(tempW), int32(tempH)
	width, height := gr.Renderer.GetResolution()
	if width == 0 || height == 0 {
		groggy.Logsf("ERROR", "GameRenderer.DoRender() window size w/h: %d / %d", width, height)
	}
	if width != currentWidth || height != currentHeight {
		width, height = currentWidth, currentHeight
		gr.Renderer.ChangeResolution(width, height)
	}

	// clear the screen
	gl.Viewport(0, 0, int32(width), int32(height))
	gl.ClearColor(0.05, 0.05, 0.05, 1.0)
	gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

	// make the projection and view matrixes
	perspective := mgl.Perspective(mgl.DegToRad(60.0), float32(width)/float32(height), 1.0, 400.0)
	view := gr.camera.GetViewMatrix()

	// Get the landscape chunks to render -- which for now is everything
	landMan := gameManager.GetLandscapeManager()
	allChunks := landMan.GetAllChunks()

	// draw everything you crazy devil
	for _, c := range allChunks {
		node := c.GetTheRenderable()
		if node.Core.Shader == nil {
			node.Core.Shader = gr.Shaders["landscape"]
		}
		gr.Renderer.DrawRenderable(node, customLandscapeBinder, perspective, view)
	}

	// get the entity manager and all the entities
	entMan := gameManager.GetEntityManager()
	allEnts := entMan.GetAllEntities()

	// draw everything, because we're crazy
	for _, e := range allEnts {
		// make sure we have something to draw
		if e.Renderable == nil {
			// try to clone a new renderable from the asset prototypes
			proto, okay := loadedAssets[e.AssetPath]
			if okay {
				e.Renderable = proto.Clone()
			}
		}

		// sync the loc/rot to the renderable
		e.SyncToRenderable()

		// did we sort things out and make a renderable?
		if e.Renderable != nil {
			gr.Renderer.DrawRenderable(e.Renderable, nil, perspective, view)
		}
	}

	// draw the screen
	gr.Renderer.EndRenderFrame()
}