diff --git a/examples/core/2d_camera_platformer/main.go b/examples/core/2d_camera_platformer/main.go new file mode 100644 index 0000000..a6286ad --- /dev/null +++ b/examples/core/2d_camera_platformer/main.go @@ -0,0 +1,261 @@ +package main + +import rl "github.com/gen2brain/raylib-go/raylib" + +const ( + screenWidth = 800 + screenHeight = 450 + gravity = 400 + playerJumpSpeed = 350.0 + playerHORSpeed = 200.0 +) + +type Player struct { + position rl.Vector2 + speed float32 + canJump bool +} + +type EnvironmentItem struct { + rect rl.Rectangle + blocking bool + color rl.Color +} + +type cameraUpdater func(*rl.Camera2D, *Player, []EnvironmentItem, float32) + +// These 3 variables are used only for camera 4, +// but they need to be declared on module level +// for camera 4 to work (static in C) +var eveningOut = false +var evenOutSpeed float32 = 700 +var evenOutTarget float32 = 0 + +func main() { + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera platformer") + + player := Player{ + position: rl.NewVector2(400, 280), + speed: 0, + canJump: false, + } + + envItems := []EnvironmentItem{ + {rect: rl.Rectangle{Width: 1000, Height: 400}, blocking: false, color: rl.LightGray}, + {rect: rl.Rectangle{Y: 400, Width: 1000, Height: 200}, blocking: true, color: rl.Gray}, + {rect: rl.Rectangle{X: 300, Y: 200, Width: 400, Height: 10}, blocking: true, color: rl.Gray}, + {rect: rl.Rectangle{X: 250, Y: 300, Width: 100, Height: 10}, blocking: true, color: rl.Gray}, + {rect: rl.Rectangle{X: 650, Y: 300, Width: 100, Height: 10}, blocking: true, color: rl.Gray}, + } + + camera := rl.Camera2D{ + Offset: rl.Vector2{X: screenWidth / 2, Y: screenHeight / 2}, + Target: player.position, + Rotation: 0, + Zoom: 1, + } + + cameraUpdaters := []cameraUpdater{ + updateCameraCenter, + updateCameraCenterInsideMap, + updateCameraCenterSmoothFollow, + updateCameraEvenOutOnLanding, + updateCameraPlayerBoundsPush, + } + cameraDescriptions := []string{ + "1. Follow player center", + "2. Follow player center, but clamp to map edges", + "3. Follow player center; smoothed", + "4. Follow player center horizontally; update player center vertically after landing", + "5. Player push camera on getting too close to screen edge", + } + + cameraOption := 0 + + rl.SetTargetFPS(60) + + for !rl.WindowShouldClose() { + deltaTime := rl.GetFrameTime() + + updatePlayer(&player, envItems, deltaTime) + + camera.Zoom += rl.GetMouseWheelMove() * 0.05 + + camera.Zoom = clamp(camera.Zoom, 0.25, 3) + + if rl.IsKeyPressed(rl.KeyC) { + cameraOption = (cameraOption + 1) % len(cameraUpdaters) + } + // Call update camera function by its pointer + cameraUpdaters[cameraOption](&camera, &player, envItems, deltaTime) + + // Draw + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) + rl.BeginMode2D(camera) + + for _, item := range envItems { + rl.DrawRectangleRec(item.rect, item.color) + } + + playerRect := rl.Rectangle{ + X: player.position.X - 20, + Y: player.position.Y - 40, + Width: 40, + Height: 40, + } + rl.DrawRectangleRec(playerRect, rl.Red) + rl.DrawCircleV(player.position, 5, rl.Gold) + + rl.EndMode2D() + + rl.DrawText("Controls:", 20, 20, 10, rl.Black) + rl.DrawText(" - Right/Left to move", 40, 40, 10, rl.DarkGray) + rl.DrawText(" - Space to jump", 40, 60, 10, rl.DarkGray) + rl.DrawText(" - Mouse Wheel to Zoom in-out, R to reset zoom", 40, 80, 10, rl.DarkGray) + rl.DrawText(" - C to change camera mode", 20, 100, 10, rl.Black) + rl.DrawText("Current Camera Mode:", 20, 120, 10, rl.DarkGray) + rl.DrawText(cameraDescriptions[cameraOption], 40, 140, 10, rl.DarkGray) + + rl.EndDrawing() + } + rl.CloseWindow() +} + +func updatePlayer(player *Player, envItems []EnvironmentItem, delta float32) { + if rl.IsKeyDown(rl.KeyLeft) { + player.position.X -= playerHORSpeed * delta + } + if rl.IsKeyDown(rl.KeyRight) { + player.position.X += playerHORSpeed * delta + } + if rl.IsKeyDown(rl.KeySpace) && player.canJump { + player.speed = -playerJumpSpeed + player.canJump = false + } + + hitObstacle := false + for _, item := range envItems { + if item.blocking && + item.rect.X <= player.position.X && item.rect.X+item.rect.Width >= player.position.X && + item.rect.Y >= player.position.Y && item.rect.Y <= player.position.Y+player.speed*delta { + hitObstacle = true + player.speed = 0 + player.position.Y = item.rect.Y + break + } + } + + if !hitObstacle { + player.position.Y += player.speed * delta + player.speed += gravity * delta + player.canJump = false + } else { + player.canJump = true + } +} + +func updateCameraCenter(camera *rl.Camera2D, player *Player, _ []EnvironmentItem, _ float32) { + camera.Offset = rl.Vector2{X: screenWidth / 2, Y: screenHeight / 2} + camera.Target = player.position +} + +func updateCameraCenterInsideMap(camera *rl.Camera2D, player *Player, envItems []EnvironmentItem, _ float32) { + camera.Offset = rl.Vector2{X: screenWidth / 2, Y: screenHeight / 2} + camera.Target = player.position + + var minX, minY, maxX, maxY float32 = 1000, 1000, -1000, -10000 + + for _, item := range envItems { + minX = min(item.rect.X, minX) + maxX = max(item.rect.X+item.rect.Width, maxX) + minY = min(item.rect.Y, minY) + maxY = max(item.rect.Y+item.rect.Height, maxY) + } + + maxV := rl.GetWorldToScreen2D(rl.Vector2{X: maxX, Y: maxY}, *camera) + minV := rl.GetWorldToScreen2D(rl.Vector2{X: minX, Y: minY}, *camera) + + if maxV.X < screenWidth { + camera.Offset.X = screenWidth - (maxV.X - screenWidth/2) + } + if maxV.Y < screenHeight { + camera.Offset.Y = screenHeight - (maxV.Y - screenHeight/2) + } + if minV.X > 0 { + camera.Offset.X = screenWidth/2 - minV.X + } + if minV.Y > 0 { + camera.Offset.Y = screenHeight/2 - minV.Y + } +} + +func updateCameraCenterSmoothFollow(camera *rl.Camera2D, player *Player, _ []EnvironmentItem, delta float32) { + var minSpeed, minEffectLength, fractionSpeed float32 = 30.0, 10.0, 0.8 + + camera.Offset = rl.Vector2{X: screenWidth / 2, Y: screenHeight / 2} + diff := rl.Vector2Subtract(player.position, camera.Target) + length := rl.Vector2Length(diff) + + if length > minEffectLength { + speed := max(fractionSpeed*length, minSpeed) + camera.Target = rl.Vector2Add(camera.Target, rl.Vector2Scale(diff, speed*delta/length)) + } +} + +func updateCameraEvenOutOnLanding(camera *rl.Camera2D, player *Player, _ []EnvironmentItem, delta float32) { + camera.Offset = rl.Vector2{X: screenWidth / 2, Y: screenHeight / 2} + camera.Target.X = player.position.X + + if eveningOut { + if evenOutTarget > camera.Target.Y { + camera.Target.Y += evenOutSpeed * delta + if camera.Target.Y > evenOutTarget { + camera.Target.Y = evenOutTarget + eveningOut = false + } + } else { + camera.Target.Y -= evenOutSpeed * delta + if camera.Target.Y < evenOutTarget { + camera.Target.Y = evenOutTarget + eveningOut = false + } + } + } else { + if player.canJump && player.speed == 0 && player.position.Y != camera.Target.Y { + eveningOut = true + evenOutTarget = player.position.Y + } + } +} + +func updateCameraPlayerBoundsPush(camera *rl.Camera2D, player *Player, _ []EnvironmentItem, _ float32) { + bbox := rl.Vector2{X: 0.2, Y: 0.2} + + bboxWorldMin := rl.GetScreenToWorld2D(rl.Vector2{X: (1 - bbox.X) * 0.5 * screenWidth, Y: (1 - bbox.Y) * 0.5 * screenHeight}, *camera) + bboxWorldMax := rl.GetScreenToWorld2D(rl.Vector2{X: (1 + bbox.X) * 0.5 * screenWidth, Y: (1 + bbox.Y) * 0.5 * screenHeight}, *camera) + camera.Offset = rl.Vector2{X: (1 - bbox.X) * 0.5 * screenWidth, Y: (1 - bbox.Y) * 0.5 * screenHeight} + + if player.position.X < bboxWorldMin.X { + camera.Target.X = player.position.X + } + if player.position.Y < bboxWorldMin.Y { + camera.Target.Y = player.position.Y + } + if player.position.X > bboxWorldMax.X { + camera.Target.X = bboxWorldMin.X + (player.position.X - bboxWorldMax.X) + } + if player.position.Y > bboxWorldMax.Y { + camera.Target.Y = bboxWorldMin.Y + (player.position.Y - bboxWorldMax.Y) + } +} + +func clamp(zoom float32, min float32, max float32) float32 { + if zoom < min { + return min + } + if zoom > max { + return max + } + return zoom +} diff --git a/examples/core/custom_frame_control/main.go b/examples/core/custom_frame_control/main.go new file mode 100644 index 0000000..7dfc374 --- /dev/null +++ b/examples/core/custom_frame_control/main.go @@ -0,0 +1,131 @@ +package main + +/******************************************************************************************* +* +* raylib [core] example - custom frame control +* +* NOTE: WARNING: This is an example for advanced users willing to have full control over +* the frame processes. By default, EndDrawing() calls the following processes: +* 1. Draw remaining batch data: rlDrawRenderBatchActive() +* 2. SwapScreenBuffer() +* 3. Frame time control: WaitTime() +* 4. PollInputEvents() +* +* To avoid steps 2, 3 and 4, flag SUPPORT_CUSTOM_FRAME_CONTROL can be enabled in +* config.h (it requires recompiling raylib). This way those steps are up to the user. +* +* Note that enabling this flag invalidates some functions: +* - GetFrameTime() +* - SetTargetFPS() +* - GetFPS() +* +* Example originally created with raylib 4.0, last time updated with raylib 4.0 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2021-2024 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +import ( + "fmt" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +const ( + screenWidth = 800 + screenHeight = 450 +) + +func main() { + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - Custom Frame Control") + + previousTime := rl.GetTime() + currentTime := 0.0 + updateDrawTime := 0.0 + waitTime := 0.0 + deltaTime := 0.0 + + timeCounter := 0.0 + position := 0.0 + pause := false + + targetFPS := 60 + + for !rl.WindowShouldClose() { + rl.PollInputEvents() // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL) + + if rl.IsKeyPressed(rl.KeySpace) { + pause = !pause + } + if rl.IsKeyPressed(rl.KeyUp) { + targetFPS += 20 + } else if rl.IsKeyPressed(rl.KeyDown) { + targetFPS -= 20 + } + + if targetFPS < 0 { + targetFPS = 0 + } + + if !pause { + position += 200 * deltaTime // We move at 200 pixels per second + if position >= float64(rl.GetScreenWidth()) { + position = 0 + } + timeCounter += deltaTime // We count time (seconds) + } + + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) + + for i := 0; i < rl.GetScreenWidth()/200; i++ { + rl.DrawRectangle(200*int32(i), 0, 1, int32(rl.GetScreenHeight()), rl.SkyBlue) + } + + rl.DrawCircle(int32(position), int32(rl.GetScreenHeight()/2-25), 50, rl.Red) + + msg := fmt.Sprintf("%03.0f ms", timeCounter*1000) + rl.DrawText(msg, int32(position-40), int32(rl.GetScreenHeight()/2-100), 20, rl.Maroon) + msg = fmt.Sprintf("PosX: %03.0f", position) + rl.DrawText(msg, int32(position-50), int32(rl.GetScreenHeight()/2+40), 20, rl.Black) + + msg = "Circle is moving at a constant 200 pixels/sec,\nindependently of the frame rate." + rl.DrawText(msg, 10, 10, 20, rl.DarkGray) + msg = "PRESS SPACE to PAUSE MOVEMENT" + rl.DrawText(msg, 10, int32(rl.GetScreenHeight()-60), 20, rl.Gray) + msg = "PRESS UP | DOWN to CHANGE TARGET FPS" + rl.DrawText(msg, 10, int32(rl.GetScreenHeight()-30), 20, rl.Gray) + msg = fmt.Sprintf("TARGET FPS: %d", targetFPS) + rl.DrawText(msg, int32(rl.GetScreenWidth()-220), 10, 20, rl.Lime) + msg = fmt.Sprintf("CURRENT FPS: %d", int(1/deltaTime)) + rl.DrawText(msg, int32(rl.GetScreenWidth()-220), 40, 20, rl.Lime) + + rl.EndDrawing() + + // NOTE: In case raylib is configured to SUPPORT_CUSTOM_FRAME_CONTROL, + // Events polling, screen buffer swap and frame time control must be managed by the user + + rl.SwapScreenBuffer() // We want a fixed frame rate + + currentTime = rl.GetTime() + updateDrawTime = currentTime - previousTime + + if targetFPS > 0 { // We want a fixed frame rate + waitTime = (1 / float64(targetFPS)) - updateDrawTime + if waitTime > 0 { + rl.WaitTime(waitTime) + currentTime = rl.GetTime() + deltaTime = currentTime - previousTime + } + } else { + deltaTime = updateDrawTime + } + + previousTime = currentTime + } + + rl.CloseWindow() +} diff --git a/examples/core/custom_logging/main.go b/examples/core/custom_logging/main.go new file mode 100644 index 0000000..785590e --- /dev/null +++ b/examples/core/custom_logging/main.go @@ -0,0 +1,49 @@ +package main + +import ( + "fmt" + "time" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +const ( + screenWidth = 800 + screenHeight = 450 +) + +func main() { + // Set custom logger + rl.SetTraceLogCallback(customLog) + + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - Custom Logging") + + rl.SetTargetFPS(60) + + for !rl.WindowShouldClose() { + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) + rl.DrawText("Check the console output to see the custom logger in action!", 60, 200, 20, rl.LightGray) + rl.EndDrawing() + } + + rl.CloseWindow() +} + +// Custom logging function +func customLog(msgType int, text string) { + now := time.Now() + + switch rl.TraceLogLevel(msgType) { + case rl.LogInfo: + fmt.Println("[INFO] : ", now, text) + case rl.LogError: + fmt.Println("[ERROR] : ", now, text) + case rl.LogWarning: + fmt.Println("[WARNING] : ", now, text) + case rl.LogDebug: + fmt.Println("[DEBUG] : ", now, text) + default: + fmt.Println("[UNKNOWN] : ", now, text) + } +} diff --git a/examples/core/window_flags/main.go b/examples/core/window_flags/main.go new file mode 100644 index 0000000..6e26ebe --- /dev/null +++ b/examples/core/window_flags/main.go @@ -0,0 +1,260 @@ +/******************************************************************************************* +* +* raylib [core] example - window flags +* +* Example originally created with raylib 3.5, last time updated with raylib 3.5 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2020-2024 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +package main + +import ( + "fmt" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +const ( + screenWidth = 800 + screenHeight = 450 +) + +// Possible window flags +/* + FLAG_VSYNC_HINT + FLAG_FULLSCREEN_MODE -> not working properly -> wrong scaling! + FLAG_WINDOW_RESIZABLE + FLAG_WINDOW_UNDECORATED + FLAG_WINDOW_TRANSPARENT + FLAG_WINDOW_HIDDEN + FLAG_WINDOW_MINIMIZED -> Not supported on window creation + FLAG_WINDOW_MAXIMIZED -> Not supported on window creation + FLAG_WINDOW_UNFOCUSED + FLAG_WINDOW_TOPMOST + FLAG_WINDOW_HIGHDPI -> errors after minimize-resize, fb size is recalculated + FLAG_WINDOW_ALWAYS_RUN + FLAG_MSAA_4X_HINT +*/ + +func main() { + // Set configuration flags for window creation + //SetConfigFlags(FLAG_VSYNC_HINT | FLAG_MSAA_4X_HINT | FLAG_WINDOW_HIGHDPI); + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - Window Flags") + + ballPosition := rl.Vector2{X: float32(rl.GetScreenWidth()) / 2.0, Y: float32(rl.GetScreenHeight()) / 2.0} + ballSpeed := rl.Vector2{X: 5.0, Y: 4.0} + ballRadius := float32(20.0) + framesCounter := 0 + + rl.SetTargetFPS(60) // Set our game to run at 60 frames-per-second + + for !rl.WindowShouldClose() { // Detect window close button or ESC key + if rl.IsKeyPressed(rl.KeyF) { + rl.ToggleFullscreen() // modifies window size when scaling! + } + + if rl.IsKeyPressed(rl.KeyR) { + if rl.IsWindowState(rl.FlagWindowResizable) { + rl.ClearWindowState(rl.FlagWindowResizable) + } else { + rl.SetWindowState(rl.FlagWindowResizable) + } + } + + if rl.IsKeyPressed(rl.KeyD) { + if rl.IsWindowState(rl.FlagWindowUndecorated) { + rl.ClearWindowState(rl.FlagWindowUndecorated) + } else { + rl.SetWindowState(rl.FlagWindowUndecorated) + } + } + + if rl.IsKeyPressed(rl.KeyH) { + if !rl.IsWindowState(rl.FlagWindowHidden) { + rl.SetWindowState(rl.FlagWindowHidden) + } + + framesCounter = 0 + } + + if rl.IsWindowState(rl.FlagWindowHidden) { + framesCounter++ + if framesCounter >= 240 { + rl.ClearWindowState(rl.FlagWindowHidden) // Show window after 3 seconds + } + } + + if rl.IsKeyPressed(rl.KeyN) { + if !rl.IsWindowState(rl.FlagWindowMinimized) { + rl.MinimizeWindow() + } + + framesCounter = 0 + } + + if rl.IsWindowState(rl.FlagWindowMinimized) { + framesCounter++ + if framesCounter >= 240 { + rl.RestoreWindow() // Restore window after 3 seconds + } + } + + if rl.IsKeyPressed(rl.KeyM) { + // NOTE: Requires FLAG_WINDOW_RESIZABLE enabled! + if rl.IsWindowState(rl.FlagWindowMaximized) { + rl.RestoreWindow() + } else { + rl.MaximizeWindow() + } + } + + if rl.IsKeyPressed(rl.KeyU) { + if rl.IsWindowState(rl.FlagWindowUnfocused) { + rl.ClearWindowState(rl.FlagWindowUnfocused) + } else { + rl.SetWindowState(rl.FlagWindowUnfocused) + } + } + + if rl.IsKeyPressed(rl.KeyT) { + if rl.IsWindowState(rl.FlagWindowTopmost) { + rl.ClearWindowState(rl.FlagWindowTopmost) + } else { + rl.SetWindowState(rl.FlagWindowTopmost) + } + } + + if rl.IsKeyPressed(rl.KeyA) { + if rl.IsWindowState(rl.FlagWindowAlwaysRun) { + rl.ClearWindowState(rl.FlagWindowAlwaysRun) + } else { + rl.SetWindowState(rl.FlagWindowAlwaysRun) + } + } + + if rl.IsKeyPressed(rl.KeyV) { + if rl.IsWindowState(rl.FlagVsyncHint) { + rl.ClearWindowState(rl.FlagVsyncHint) + } else { + rl.SetWindowState(rl.FlagVsyncHint) + } + } + + // Bouncing ball logic + ballPosition.X += ballSpeed.X + ballPosition.Y += ballSpeed.Y + if (ballPosition.X >= (float32(rl.GetScreenWidth()) - ballRadius)) || (ballPosition.X <= ballRadius) { + ballSpeed.X *= -1.0 + } + if (ballPosition.Y >= (float32(rl.GetScreenHeight()) - ballRadius)) || (ballPosition.Y <= ballRadius) { + ballSpeed.Y *= -1.0 + } + + rl.BeginDrawing() + if rl.IsWindowState(rl.FlagWindowTransparent) { + rl.ClearBackground(rl.Blank) + } else { + rl.ClearBackground(rl.White) + } + + rl.DrawCircleV(ballPosition, ballRadius, rl.Maroon) + rl.DrawRectangleLinesEx( + rl.Rectangle{ + Width: float32(rl.GetScreenWidth()), + Height: float32(rl.GetScreenHeight()), + }, + 4, rl.White, + ) + + rl.DrawCircleV(rl.GetMousePosition(), 10, rl.DarkBlue) + + rl.DrawFPS(10, 10) + + rl.DrawText( + fmt.Sprintf("Screen Size: [%d, %d]", rl.GetScreenWidth(), rl.GetScreenHeight()), + 10, + 40, + 10, + rl.Green, + ) + + // Draw window state info + rl.DrawText("Following flags can be set after window creation:", 10, 60, 10, rl.Gray) + if rl.IsWindowState(rl.FlagFullscreenMode) { + rl.DrawText("[F] FLAG_FULLSCREEN_MODE: on", 10, 80, 10, rl.Lime) + } else { + rl.DrawText("[F] FLAG_FULLSCREEN_MODE: off", 10, 80, 10, rl.Maroon) + } + if rl.IsWindowState(rl.FlagWindowResizable) { + rl.DrawText("[R] FLAG_WINDOW_RESIZABLE: on", 10, 100, 10, rl.Lime) + } else { + rl.DrawText("[R] FLAG_WINDOW_RESIZABLE: off", 10, 100, 10, rl.Maroon) + } + if rl.IsWindowState(rl.FlagWindowUndecorated) { + rl.DrawText("[D] FLAG_WINDOW_UNDECORATED: on", 10, 120, 10, rl.Lime) + } else { + rl.DrawText("[D] FLAG_WINDOW_UNDECORATED: off", 10, 120, 10, rl.Maroon) + } + if rl.IsWindowState(rl.FlagWindowHidden) { + rl.DrawText("[H] FLAG_WINDOW_HIDDEN: on", 10, 140, 10, rl.Lime) + } else { + rl.DrawText("[H] FLAG_WINDOW_HIDDEN: off", 10, 140, 10, rl.Maroon) + } + if rl.IsWindowState(rl.FlagWindowMinimized) { + rl.DrawText("[N] FLAG_WINDOW_MINIMIZED: on", 10, 160, 10, rl.Lime) + } else { + rl.DrawText("[N] FLAG_WINDOW_MINIMIZED: off", 10, 160, 10, rl.Maroon) + } + if rl.IsWindowState(rl.FlagWindowMaximized) { + rl.DrawText("[M] FLAG_WINDOW_MAXIMIZED: on", 10, 180, 10, rl.Lime) + } else { + rl.DrawText("[M] FLAG_WINDOW_MAXIMIZED: off", 10, 180, 10, rl.Maroon) + } + if rl.IsWindowState(rl.FlagWindowUnfocused) { + rl.DrawText("[G] FLAG_WINDOW_UNFOCUSED: on", 10, 200, 10, rl.Lime) + } else { + rl.DrawText("[U] FLAG_WINDOW_UNFOCUSED: off", 10, 200, 10, rl.Maroon) + } + if rl.IsWindowState(rl.FlagWindowTopmost) { + rl.DrawText("[T] FLAG_WINDOW_TOPMOST: on", 10, 220, 10, rl.Lime) + } else { + rl.DrawText("[T] FLAG_WINDOW_TOPMOST: off", 10, 220, 10, rl.Maroon) + } + if rl.IsWindowState(rl.FlagWindowAlwaysRun) { + rl.DrawText("[A] FLAG_WINDOW_ALWAYS_RUN: on", 10, 240, 10, rl.Lime) + } else { + rl.DrawText("[A] FLAG_WINDOW_ALWAYS_RUN: off", 10, 240, 10, rl.Maroon) + } + if rl.IsWindowState(rl.FlagVsyncHint) { + rl.DrawText("[V] FLAG_VSYNC_HINT: on", 10, 260, 10, + rl.Lime) + } else { + rl.DrawText("[V] FLAG_VSYNC_HINT: off", 10, 260, 10, rl.Maroon) + } + + rl.DrawText("Following flags can only be set before window creation:", 10, 300, 10, rl.Gray) + if rl.IsWindowState(rl.FlagWindowHighdpi) { + rl.DrawText("FLAG_WINDOW_HIGHDPI: on", 10, 320, 10, rl.Lime) + } else { + rl.DrawText("FLAG_WINDOW_HIGHDPI: off", 10, 320, 10, rl.Maroon) + } + if rl.IsWindowState(rl.FlagWindowTransparent) { + rl.DrawText("FLAG_WINDOW_TRANSPARENT: on", 10, 340, 10, rl.Lime) + } else { + rl.DrawText("FLAG_WINDOW_TRANSPARENT: off", 10, 340, 10, rl.Maroon) + } + if rl.IsWindowState(rl.FlagMsaa4xHint) { + rl.DrawText("FLAG_MSAA_4X_HINT: on", 10, 360, 10, rl.Lime) + } else { + rl.DrawText("FLAG_MSAA_4X_HINT: off", 10, 360, 10, rl.Maroon) + } + + rl.EndDrawing() + } + rl.CloseWindow() +} diff --git a/examples/core/window_letterbox/main.go b/examples/core/window_letterbox/main.go new file mode 100644 index 0000000..ef7518f --- /dev/null +++ b/examples/core/window_letterbox/main.go @@ -0,0 +1,115 @@ +package main + +import ( + "fmt" + "image/color" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +const ( + screenWidth = 800 + screenHeight = 450 +) + +func main() { + // Enable config flags for resizable window and vertical synchro + rl.SetConfigFlags(rl.FlagWindowResizable | rl.FlagVsyncHint) + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - Window Scale Letterbox") + rl.SetWindowMinSize(320, 240) + + gameScreenWidth, gameScreenHeight := int32(640), int32(480) + + // Render texture initialization, used to hold the rendering result so we can easily resize it + target := rl.LoadRenderTexture(gameScreenWidth, gameScreenHeight) + rl.SetTextureFilter(target.Texture, rl.FilterBilinear) // Texture scale filter to use + + colors := getRandomColors() + + rl.SetTargetFPS(60) // Set our game to run at 60 frames-per-second + + for !rl.WindowShouldClose() { // Detect window close button or ESC key + // Compute required frame buffer scaling + scale := min(float32(rl.GetScreenWidth())/float32(gameScreenWidth), + float32(rl.GetScreenHeight())/float32(gameScreenHeight)) + + if rl.IsKeyPressed(rl.KeySpace) { + // Recalculate random colors for the bars + colors = getRandomColors() + } + + // Update virtual mouse (clamped mouse value behind game screen) + mouse := rl.GetMousePosition() + virtualMouse := rl.Vector2{ + X: (mouse.X - (float32(rl.GetScreenWidth())-(float32(gameScreenWidth)*scale))*0.5) / scale, + Y: (mouse.Y - (float32(rl.GetScreenHeight())-(float32(gameScreenHeight)*scale))*0.5) / scale, + } + virtualMouse = rl.Vector2Clamp( + virtualMouse, + rl.Vector2{}, + rl.Vector2{X: float32(gameScreenWidth), Y: float32(gameScreenHeight)}, + ) + + // Apply the same transformation as the virtual mouse to the real mouse (i.e. to work with raygui) + //rl.SetMouseOffset( + // int(-(float32(rl.GetScreenWidth())-(float32(gameScreenWidth)*scale))*0.5), + // int(-(float32(rl.GetScreenHeight())-(float32(gameScreenHeight)*scale))*0.5), + //) + //rl.SetMouseScale(1/scale, 1/scale) + + // Draw everything in the render texture, note this will not be rendered on screen, yet + rl.BeginTextureMode(target) + + rl.ClearBackground(rl.White) + for i := int32(0); i < 10; i++ { + rl.DrawRectangle(0, (gameScreenHeight/10)*i, gameScreenWidth, gameScreenHeight/10, colors[i]) + } + + text := "If executed inside a window,\nyou can resize the window,\nand see the screen scaling!" + rl.DrawText(text, 10, 25, 20, rl.White) + text = fmt.Sprintf("Default Mouse: [%.0f , %.0f]", mouse.X, mouse.Y) + rl.DrawText(text, 350, 25, 20, rl.Green) + text = fmt.Sprintf("Virtual Mouse: [%.0f , %.0f]", virtualMouse.X, virtualMouse.Y) + rl.DrawText(text, 350, 55, 20, rl.Yellow) + + rl.EndTextureMode() + + rl.BeginDrawing() + rl.ClearBackground(rl.Black) + // Draw render texture to screen, properly scaled + rl.DrawTexturePro( + target.Texture, + rl.Rectangle{Width: float32(target.Texture.Width), Height: float32(-target.Texture.Height)}, + rl.Rectangle{ + X: (float32(rl.GetScreenWidth()) - float32(gameScreenWidth)*scale) * 0.5, + Y: (float32(rl.GetScreenHeight()) - float32(gameScreenHeight)*scale) * 0.5, + Width: float32(gameScreenWidth) * scale, + Height: float32(gameScreenHeight) * scale, + }, + rl.Vector2{X: 0, Y: 0}, 0, rl.White, + ) + rl.EndDrawing() + } + rl.UnloadRenderTexture(target) + rl.CloseWindow() // Close window and OpenGL context +} + +func getRandomColors() []color.RGBA { + var colors []color.RGBA + + for i := 0; i < 10; i++ { + randomColor := color.RGBA{ + R: rndUint8(100, 250), + G: rndUint8(50, 150), + B: rndUint8(10, 100), + A: 255, + } + colors = append(colors, randomColor) + } + + return colors +} + +func rndUint8(min, max int32) uint8 { + return uint8(rl.GetRandomValue(min, max)) +} diff --git a/examples/core/window_should_close/main.go b/examples/core/window_should_close/main.go new file mode 100644 index 0000000..19324d1 --- /dev/null +++ b/examples/core/window_should_close/main.go @@ -0,0 +1,47 @@ +package main + +import rl "github.com/gen2brain/raylib-go/raylib" + +const ( + screenWidth = 850 + screenHeight = 480 +) + +func main() { + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - window should close") + + rl.SetExitKey(rl.KeyNull) // Disable KEY_ESCAPE to close window, X-button still works + + exitWindowRequested := false // Flag to request window to exit + exitWindow := false // Flag to set window to exit + + rl.SetTargetFPS(60) // Set our game to run at 60 frames-per-second + + for !exitWindow { + if rl.WindowShouldClose() || rl.IsMouseButtonPressed(rl.KeyEscape) { + exitWindowRequested = true + } + + if exitWindowRequested { + if rl.IsKeyPressed(rl.KeyY) { + exitWindow = true + } else if rl.IsKeyPressed(rl.KeyN) { + exitWindowRequested = false + } + } + + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) + + if exitWindowRequested { + rl.DrawRectangle(0, 100, screenWidth, 200, rl.Black) + rl.DrawText("Are you sure you want to exit the program? [Y/N]", 40, 180, 30, rl.White) + } else { + rl.DrawText("Try to close the window to get confirmation message!", 120, 200, 20, rl.LightGray) + } + + rl.EndDrawing() + } + + rl.CloseWindow() // Close window and OpenGL context +}