Compare commits
9 Commits
v1.1.1
...
7be859d58f
Author | SHA1 | Date | |
---|---|---|---|
7be859d58f | |||
fde07e3e48 | |||
f966d538d3 | |||
220a451475 | |||
417bf4ea63 | |||
84d63ba4c1 | |||
49b84c8540 | |||
0e509ad752 | |||
bcd63efd7b |
4
.gitignore
vendored
4
.gitignore
vendored
@ -10,4 +10,6 @@ goonscape.exe
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
Thumbs.db
|
||||
|
||||
resources/models/old_and_test/
|
@ -5,22 +5,79 @@ import (
|
||||
rl "github.com/gen2brain/raylib-go/raylib"
|
||||
)
|
||||
|
||||
// Helper function to load animations for a model
|
||||
func loadModelAnimations(animPaths map[string]string) (types.AnimationSet, error) {
|
||||
var animSet types.AnimationSet
|
||||
|
||||
// Load idle animations if specified
|
||||
if idlePath, ok := animPaths["idle"]; ok {
|
||||
idleAnims := rl.LoadModelAnimations(idlePath)
|
||||
if len(idleAnims) > 0 {
|
||||
animSet.Idle = idleAnims
|
||||
rl.TraceLog(rl.LogInfo, "Loaded idle animation: %s (%d frames, %f seconds)",
|
||||
idlePath, idleAnims[0].FrameCount, float32(idleAnims[0].FrameCount)/60.0)
|
||||
}
|
||||
}
|
||||
|
||||
// Load walk animations if specified
|
||||
if walkPath, ok := animPaths["walk"]; ok {
|
||||
walkAnims := rl.LoadModelAnimations(walkPath)
|
||||
if len(walkAnims) > 0 {
|
||||
animSet.Walk = walkAnims
|
||||
rl.TraceLog(rl.LogInfo, "Loaded walk animation: %s (%d frames, %f seconds)",
|
||||
walkPath, walkAnims[0].FrameCount, float32(walkAnims[0].FrameCount)/60.0)
|
||||
}
|
||||
}
|
||||
|
||||
return animSet, nil
|
||||
}
|
||||
|
||||
func LoadModels() ([]types.ModelAsset, error) {
|
||||
goonerModel := rl.LoadModel("resources/models/goonion.obj")
|
||||
goonerTexture := rl.LoadTexture("resources/models/goonion.png")
|
||||
rl.SetMaterialTexture(goonerModel.Materials, rl.MapDiffuse, goonerTexture)
|
||||
// Goonion model and animations
|
||||
goonerModel := rl.LoadModel("resources/models/gooner/walk_no_y_transform.glb")
|
||||
goonerAnims, _ := loadModelAnimations(map[string]string{"idle": "resources/models/gooner/idle_no_y_transform.glb", "walk": "resources/models/gooner/walk_no_y_transform.glb"})
|
||||
|
||||
coomerModel := rl.LoadModel("resources/models/coomer.obj")
|
||||
coomerTexture := rl.LoadTexture("resources/models/coomer.png")
|
||||
rl.SetMaterialTexture(coomerModel.Materials, rl.MapDiffuse, coomerTexture)
|
||||
// Apply transformations
|
||||
transform := rl.MatrixIdentity()
|
||||
transform = rl.MatrixMultiply(transform, rl.MatrixRotateY(180*rl.Deg2rad))
|
||||
transform = rl.MatrixMultiply(transform, rl.MatrixRotateX(-90*rl.Deg2rad))
|
||||
transform = rl.MatrixMultiply(transform, rl.MatrixScale(1.0, 1.0, 1.0))
|
||||
goonerModel.Transform = transform
|
||||
|
||||
// Coomer model (ready for animations)
|
||||
coomerModel := rl.LoadModel("resources/models/coomer/idle_notransy.glb")
|
||||
// coomerTexture := rl.LoadTexture("resources/models/coomer.png")
|
||||
// rl.SetMaterialTexture(coomerModel.Materials, rl.MapDiffuse, coomerTexture)
|
||||
// When you have animations, add them like:
|
||||
coomerAnims, _ := loadModelAnimations(map[string]string{"idle": "resources/models/coomer/idle_notransy.glb", "walk": "resources/models/coomer/unsteadywalk_notransy.glb"})
|
||||
coomerModel.Transform = transform
|
||||
|
||||
// Shreke model (ready for animations)
|
||||
shrekeModel := rl.LoadModel("resources/models/shreke.obj")
|
||||
shrekeTexture := rl.LoadTexture("resources/models/shreke.png")
|
||||
rl.SetMaterialTexture(shrekeModel.Materials, rl.MapDiffuse, shrekeTexture)
|
||||
// When you have animations, add them like:
|
||||
// shrekeAnims, _ := loadModelAnimations("resources/models/shreke.glb",
|
||||
// map[string]string{
|
||||
// "idle": "resources/models/shreke_idle.glb",
|
||||
// "walk": "resources/models/shreke_walk.glb",
|
||||
// })
|
||||
|
||||
return []types.ModelAsset{
|
||||
{Model: goonerModel, Texture: goonerTexture},
|
||||
{Model: coomerModel, Texture: coomerTexture},
|
||||
{
|
||||
Model: goonerModel,
|
||||
Animation: append(goonerAnims.Idle, goonerAnims.Walk...),
|
||||
AnimFrames: int32(len(goonerAnims.Idle) + len(goonerAnims.Walk)),
|
||||
Animations: goonerAnims,
|
||||
YOffset: 0.0,
|
||||
},
|
||||
{
|
||||
Model: coomerModel,
|
||||
Animation: append(coomerAnims.Idle, coomerAnims.Walk...),
|
||||
AnimFrames: int32(len(coomerAnims.Idle) + len(coomerAnims.Walk)),
|
||||
Animations: coomerAnims,
|
||||
YOffset: -4.0,
|
||||
},
|
||||
{Model: shrekeModel, Texture: shrekeTexture},
|
||||
}, nil
|
||||
}
|
||||
@ -31,6 +88,11 @@ func LoadMusic(filename string) (rl.Music, error) {
|
||||
|
||||
func UnloadModels(models []types.ModelAsset) {
|
||||
for _, model := range models {
|
||||
if model.Animation != nil {
|
||||
for i := int32(0); i < model.AnimFrames; i++ {
|
||||
rl.UnloadModelAnimation(model.Animation[i])
|
||||
}
|
||||
}
|
||||
rl.UnloadModel(model.Model)
|
||||
rl.UnloadTexture(model.Texture)
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ var (
|
||||
cameraDistance = float32(20.0)
|
||||
cameraYaw = float32(145.0)
|
||||
cameraPitch = float32(45.0)
|
||||
lastMousePos rl.Vector2 // Add this to track mouse movement
|
||||
)
|
||||
|
||||
func UpdateCamera(camera *rl.Camera3D, player rl.Vector3, deltaTime float32) {
|
||||
@ -32,6 +33,34 @@ func UpdateCamera(camera *rl.Camera3D, player rl.Vector3, deltaTime float32) {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle middle mouse camera rotation
|
||||
if rl.IsMouseButtonDown(rl.MouseMiddleButton) {
|
||||
currentMousePos := rl.GetMousePosition()
|
||||
|
||||
// If we just started holding the button, initialize last position
|
||||
if !rl.IsMouseButtonPressed(rl.MouseMiddleButton) {
|
||||
mouseDelta := rl.Vector2{
|
||||
X: currentMousePos.X - lastMousePos.X,
|
||||
Y: currentMousePos.Y - lastMousePos.Y,
|
||||
}
|
||||
|
||||
// Adjust rotation speed as needed
|
||||
cameraYaw += mouseDelta.X * 0.5 * deltaTime * 60
|
||||
cameraPitch += mouseDelta.Y * 0.5 * deltaTime * 60
|
||||
|
||||
// Clamp pitch to prevent camera flipping
|
||||
if cameraPitch < 20 {
|
||||
cameraPitch = 20
|
||||
}
|
||||
if cameraPitch > 85 {
|
||||
cameraPitch = 85
|
||||
}
|
||||
}
|
||||
|
||||
lastMousePos = currentMousePos
|
||||
}
|
||||
|
||||
// Keep the keyboard controls too
|
||||
if rl.IsKeyDown(rl.KeyRight) {
|
||||
cameraYaw += 100 * deltaTime
|
||||
}
|
||||
|
41
game/game.go
41
game/game.go
@ -68,17 +68,14 @@ func (g *Game) Update(deltaTime float32) {
|
||||
return
|
||||
}
|
||||
|
||||
// Assign model based on player ID
|
||||
modelIndex := int(playerID) % len(g.Models)
|
||||
g.Player = &types.Player{
|
||||
Speed: 50.0,
|
||||
TargetPath: []types.Tile{},
|
||||
UserData: g,
|
||||
QuitDone: make(chan struct{}),
|
||||
ID: playerID,
|
||||
Model: g.Models[modelIndex].Model,
|
||||
Texture: g.Models[modelIndex].Texture,
|
||||
}
|
||||
g.AssignModelToPlayer(g.Player)
|
||||
|
||||
go network.HandleServerCommunication(conn, playerID, g.Player, g.OtherPlayers, g.QuitChan)
|
||||
g.isLoggedIn = true
|
||||
@ -157,14 +154,32 @@ func (g *Game) DrawPlayer(player *types.Player, model rl.Model) {
|
||||
defer player.Unlock()
|
||||
|
||||
grid := GetMapGrid()
|
||||
modelIndex := int(player.ID) % len(g.Models)
|
||||
modelAsset := g.Models[modelIndex]
|
||||
|
||||
const defaultHeight = 8.0 // Default height above tile, fine tune per model in types.ModelAsset
|
||||
playerPos := rl.Vector3{
|
||||
X: player.PosActual.X,
|
||||
Y: grid[player.PosTile.X][player.PosTile.Y].Height*types.TileHeight + 16.0,
|
||||
Y: grid[player.PosTile.X][player.PosTile.Y].Height*types.TileHeight + defaultHeight + modelAsset.YOffset,
|
||||
Z: player.PosActual.Z,
|
||||
}
|
||||
|
||||
// Check if model has animations
|
||||
if modelAsset.Animations.Idle != nil || modelAsset.Animations.Walk != nil {
|
||||
if player.IsMoving && len(modelAsset.Animations.Walk) > 0 {
|
||||
anim := modelAsset.Animations.Walk[0] // Use first walk animation
|
||||
player.AnimationFrame = player.AnimationFrame % anim.FrameCount
|
||||
rl.UpdateModelAnimation(model, anim, player.AnimationFrame)
|
||||
} else if len(modelAsset.Animations.Idle) > 0 {
|
||||
anim := modelAsset.Animations.Idle[0] // Use first idle animation
|
||||
player.AnimationFrame = player.AnimationFrame % anim.FrameCount
|
||||
rl.UpdateModelAnimation(model, anim, player.AnimationFrame)
|
||||
}
|
||||
}
|
||||
|
||||
rl.DrawModel(model, playerPos, 16, rl.White)
|
||||
|
||||
// Draw floating messages and path indicators
|
||||
if player.FloatingMessage != nil {
|
||||
screenPos := rl.GetWorldToScreen(rl.Vector3{
|
||||
X: playerPos.X,
|
||||
@ -207,12 +222,9 @@ func (g *Game) Render() {
|
||||
rl.BeginMode3D(g.Camera)
|
||||
g.DrawMap()
|
||||
g.DrawPlayer(g.Player, g.Player.Model)
|
||||
for id, other := range g.OtherPlayers {
|
||||
for _, other := range g.OtherPlayers {
|
||||
if other.Model.Meshes == nil {
|
||||
// Assign model based on player ID for consistency
|
||||
modelIndex := int(id) % len(g.Models)
|
||||
other.Model = g.Models[modelIndex].Model
|
||||
other.Texture = g.Models[modelIndex].Texture
|
||||
g.AssignModelToPlayer(other)
|
||||
}
|
||||
g.DrawPlayer(other, other.Model)
|
||||
}
|
||||
@ -352,3 +364,12 @@ func (g *Game) Shutdown() {
|
||||
func (g *Game) HandleServerMessages(messages []*pb.ChatMessage) {
|
||||
g.Chat.HandleServerMessages(messages)
|
||||
}
|
||||
|
||||
func (g *Game) AssignModelToPlayer(player *types.Player) {
|
||||
modelIndex := int(player.ID) % len(g.Models)
|
||||
modelAsset := g.Models[modelIndex]
|
||||
|
||||
// Just use the original model - don't try to copy it
|
||||
player.Model = modelAsset.Model
|
||||
player.Texture = modelAsset.Texture
|
||||
}
|
||||
|
BIN
resources/models/coomer/Animation_Confused_Scratch_withSkin.glb
Normal file
BIN
resources/models/coomer/Animation_Confused_Scratch_withSkin.glb
Normal file
Binary file not shown.
BIN
resources/models/coomer/Animation_Idle_withSkin.glb
Normal file
BIN
resources/models/coomer/Animation_Idle_withSkin.glb
Normal file
Binary file not shown.
BIN
resources/models/coomer/Animation_Running_withSkin.glb
Normal file
BIN
resources/models/coomer/Animation_Running_withSkin.glb
Normal file
Binary file not shown.
BIN
resources/models/coomer/Animation_Unsteady_Walk_withSkin.glb
Normal file
BIN
resources/models/coomer/Animation_Unsteady_Walk_withSkin.glb
Normal file
Binary file not shown.
BIN
resources/models/coomer/Animation_Walking_withSkin.glb
Normal file
BIN
resources/models/coomer/Animation_Walking_withSkin.glb
Normal file
Binary file not shown.
BIN
resources/models/coomer/idle_notransy.glb
Normal file
BIN
resources/models/coomer/idle_notransy.glb
Normal file
Binary file not shown.
BIN
resources/models/coomer/unsteadywalk_notransy.glb
Normal file
BIN
resources/models/coomer/unsteadywalk_notransy.glb
Normal file
Binary file not shown.
BIN
resources/models/coomerAnim.zip
Normal file
BIN
resources/models/coomerAnim.zip
Normal file
Binary file not shown.
BIN
resources/models/gooner/idle_no_y_transform.glb
Normal file
BIN
resources/models/gooner/idle_no_y_transform.glb
Normal file
Binary file not shown.
BIN
resources/models/gooner/walk_no_y_transform.glb
Normal file
BIN
resources/models/gooner/walk_no_y_transform.glb
Normal file
Binary file not shown.
@ -19,6 +19,33 @@ func (p *Player) MoveTowards(target Tile, deltaTime float32, mapGrid [][]Tile) {
|
||||
|
||||
direction := rl.Vector3Subtract(targetPos, p.PosActual)
|
||||
distance := rl.Vector3Length(direction)
|
||||
|
||||
if distance > 1.0 {
|
||||
wasMoving := p.IsMoving
|
||||
p.IsMoving = true
|
||||
|
||||
if !wasMoving {
|
||||
p.AnimationFrame = 0
|
||||
}
|
||||
|
||||
oldFrame := p.AnimationFrame
|
||||
p.AnimationFrame += int32(deltaTime * 60)
|
||||
rl.TraceLog(rl.LogInfo, "Walk frame update: %d -> %d (delta: %f)",
|
||||
oldFrame, p.AnimationFrame, deltaTime)
|
||||
} else {
|
||||
wasMoving := p.IsMoving
|
||||
p.IsMoving = false
|
||||
|
||||
if wasMoving {
|
||||
p.AnimationFrame = 0
|
||||
}
|
||||
|
||||
oldFrame := p.AnimationFrame
|
||||
p.AnimationFrame += int32(deltaTime * 60)
|
||||
rl.TraceLog(rl.LogInfo, "Idle frame update: %d -> %d (delta: %f)",
|
||||
oldFrame, p.AnimationFrame, deltaTime)
|
||||
}
|
||||
|
||||
if distance > 0 {
|
||||
direction = rl.Vector3Scale(direction, p.Speed*deltaTime/distance)
|
||||
}
|
||||
@ -41,9 +68,12 @@ func NewPlayer(state *pb.PlayerState) *Player {
|
||||
Y: float32(state.Y * TileHeight),
|
||||
Z: float32(state.Y * TileSize),
|
||||
},
|
||||
PosTile: Tile{X: int(state.X), Y: int(state.Y)},
|
||||
Speed: 50.0,
|
||||
ID: state.PlayerId,
|
||||
PosTile: Tile{X: int(state.X), Y: int(state.Y)},
|
||||
Speed: 50.0,
|
||||
ID: state.PlayerId,
|
||||
IsMoving: false,
|
||||
AnimationFrame: 0,
|
||||
LastAnimUpdate: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,15 +26,30 @@ type Player struct {
|
||||
ID int32
|
||||
CurrentTick int64
|
||||
LastUpdateTime time.Time
|
||||
LastAnimUpdate time.Time
|
||||
InterpolationProgress float32
|
||||
UserData interface{}
|
||||
FloatingMessage *FloatingMessage
|
||||
QuitDone chan struct{}
|
||||
AnimationFrame int32
|
||||
IsMoving bool
|
||||
}
|
||||
|
||||
type AnimationSet struct {
|
||||
Idle []rl.ModelAnimation
|
||||
Walk []rl.ModelAnimation
|
||||
// Can add more animation types later like:
|
||||
// Attack []ModelAnimation
|
||||
// Jump []ModelAnimation
|
||||
}
|
||||
|
||||
type ModelAsset struct {
|
||||
Model rl.Model
|
||||
Texture rl.Texture2D
|
||||
Model rl.Model
|
||||
Texture rl.Texture2D
|
||||
Animation []rl.ModelAnimation // Keep this for compatibility
|
||||
AnimFrames int32 // Keep this for compatibility
|
||||
Animations AnimationSet // New field for organized animations
|
||||
YOffset float32 // Additional height offset (added to default 8.0)
|
||||
}
|
||||
|
||||
type ChatMessage struct {
|
||||
|
Reference in New Issue
Block a user