Skip to content

Instantly share code, notes, and snippets.

@pJotoro
Created May 27, 2023 15:47
Show Gist options
  • Save pJotoro/a83c8c8271f3d5acf89031b840a0eb44 to your computer and use it in GitHub Desktop.
Save pJotoro/a83c8c8271f3d5acf89031b840a0eb44 to your computer and use it in GitHub Desktop.
Pong in Odin, no libraries
package app
import win32 "core:sys/windows"
import "core:intrinsics"
import "core:runtime"
L :: intrinsics.constant_utf16_cstring
@(private)
Context :: struct {
should_close: bool,
window: win32.HWND,
width, height: int,
keyboard_keys: [Keyboard_Key]bool,
}
@(private)
ctx: Context
@(private)
window_proc :: proc "stdcall" (window: win32.HWND, message: win32.UINT, w_param: win32.WPARAM, l_param: win32.LPARAM) -> win32.LRESULT {
result := win32.LRESULT(0)
switch message {
case win32.WM_KEYDOWN:
switch key_code := w_param; key_code {
case win32.VK_LEFT:
ctx.keyboard_keys[.Left] = true
case win32.VK_RIGHT:
ctx.keyboard_keys[.Right] = true
case win32.VK_UP:
ctx.keyboard_keys[.Up] = true
case win32.VK_DOWN:
ctx.keyboard_keys[.Down] = true
case 'A':
ctx.keyboard_keys[.A] = true
case 'W':
ctx.keyboard_keys[.W] = true
case 'S':
ctx.keyboard_keys[.S] = true
case 'D':
ctx.keyboard_keys[.D] = true
}
case win32.WM_KEYUP:
switch key_code := w_param; key_code {
case win32.VK_LEFT:
ctx.keyboard_keys[.Left] = false
case win32.VK_RIGHT:
ctx.keyboard_keys[.Right] = false
case win32.VK_UP:
ctx.keyboard_keys[.Up] = false
case win32.VK_DOWN:
ctx.keyboard_keys[.Down] = false
case 'A':
ctx.keyboard_keys[.A] = false
case 'W':
ctx.keyboard_keys[.W] = false
case 'S':
ctx.keyboard_keys[.S] = false
case 'D':
ctx.keyboard_keys[.D] = false
}
case win32.WM_CLOSE, win32.WM_DESTROY, win32.WM_QUIT:
ctx.should_close = true
case:
result = win32.DefWindowProcW(window, message, w_param, l_param)
}
return result
}
init :: proc(title: string, width, height: int) {
ctx.width = width
ctx.height = height
wtitle := win32.utf8_to_wstring(title)
instance := win32.GetModuleHandleW(L(""))
window_class := win32.WNDCLASSEXW{
cbSize = size_of(win32.WNDCLASSEXW),
lpfnWndProc = window_proc,
hInstance = cast(win32.HANDLE)instance,
lpszClassName = wtitle,
}
win32.RegisterClassExW(&window_class)
window_flags := win32.WS_OVERLAPPED | win32.WS_CAPTION | win32.WS_SYSMENU | win32.WS_VISIBLE
ctx.window = win32.CreateWindowExW(0, window_class.lpszClassName, wtitle, window_flags, win32.CW_USEDEFAULT, win32.CW_USEDEFAULT, win32.CW_USEDEFAULT, win32.CW_USEDEFAULT, nil, nil, cast(win32.HANDLE)instance, nil)
monitor := win32.MonitorFromWindow(win32.HWND(ctx.window), .MONITOR_DEFAULTTOPRIMARY)
monitor_info: win32.MONITORINFO
monitor_info.cbSize = size_of(win32.MONITORINFO)
win32.GetMonitorInfoW(monitor, &monitor_info)
monitor_width := monitor_info.rcMonitor.right - monitor_info.rcMonitor.left
monitor_height := monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top
window_rect := win32.RECT{0, 0, i32(width), i32(height)}
win32.AdjustWindowRect(&window_rect, window_flags, false)
window_width := window_rect.right - window_rect.left
window_height := window_rect.bottom - window_rect.top
window_x := (monitor_width - window_width) / 2
window_y := (monitor_height - window_height) / 2
win32.SetWindowPos(ctx.window, nil, window_x, window_y, window_width, window_height, 0)
}
should_close :: proc() -> bool {
for {
message: win32.MSG
if win32.PeekMessageW(&message, win32.HWND(ctx.window), 0, 0, win32.PM_REMOVE) {
win32.TranslateMessage(&message)
win32.DispatchMessageW(&message)
continue
}
break
}
return ctx.should_close
}
render :: proc(bitmap: []u32) {
hdc := win32.GetDC(ctx.window)
bitmap_info: win32.BITMAPINFO
bitmap_info.bmiHeader = win32.BITMAPINFOHEADER{
biSize = size_of(win32.BITMAPINFOHEADER),
biWidth = i32(ctx.width),
biHeight = i32(ctx.height),
biPlanes = 1,
biBitCount = 32,
biCompression = win32.BI_RGB,
}
win32.StretchDIBits(hdc, 0, 0, i32(ctx.width), i32(ctx.height), 0, 0, i32(ctx.width), i32(ctx.height), &bitmap[0], &bitmap_info, win32.DIB_RGB_COLORS, win32.SRCCOPY)
win32.ReleaseDC(ctx.window, hdc)
win32.timeBeginPeriod(1)
win32.Sleep(16)
win32.timeEndPeriod(1)
}
width :: proc() -> int {
return ctx.width
}
height :: proc() -> int {
return ctx.height
}
Keyboard_Key :: enum {
Left,
Right,
Up,
Down,
A,
W,
S,
D,
}
key_down :: proc(key: Keyboard_Key) -> bool {
return ctx.keyboard_keys[key]
}
package main
import "app"
import "core:slice"
//import "core:math"
draw_pixel :: proc(bitmap: []u32, color: u32, x, y: int) #no_bounds_check {
if x < 0 do return
if x >= app.width() do return
if y < 0 do return
else if y >= app.height() do return
bitmap[x + y*app.width()] = color
}
/*
draw_line :: proc(bitmap: []u32, color: u32, x0, y0, x1, y1: int) {
_x0 := min(x0, x1)
_x1 := max(x0, x1)
a := f64(y1 - y0) / f64(_x1 - _x0)
y := f64(y0)
for x in _x0..=_x1 {
draw_pixel(bitmap, color, x, int(math.floor(y)))
y += a
}
}
*/
draw_rectangle1 :: proc(bitmap: []u32, color: u32, x, y, width, height: int) {
for x in x..<x+width {
for y in y..<y+height {
draw_pixel(bitmap, color, x, y)
}
}
}
draw_rectangle2 :: proc(bitmap: []u32, color: u32, rectangle: Rectangle) {
draw_rectangle1(bitmap, color, rectangle.x, rectangle.y, rectangle.width, rectangle.height)
}
draw_rectangle :: proc{draw_rectangle1, draw_rectangle2}
Rectangle :: struct {
x, y, width, height: int,
}
touching :: proc(a, b: Rectangle) -> bool {
for x in a.x..=a.x+a.width {
for y in a.y..=a.y+a.height {
if x >= b.x && x <= b.x+b.width && y >= b.y && y <= b.y+b.height do return true
}
}
return false
}
main :: proc() {
app.init("Hello, world!", 1280, 720)
bitmap := make([]u32, app.width() * app.height())
player1 := Rectangle{0, (app.height()-150)/2, 30, 150}
player2 := Rectangle{app.width()-30, (app.height()-150)/2, 30, 150}
ball := Rectangle{(app.width()-30)/2, (app.height()-30)/2, 30, 30}
ball_dir := [2]int{10, 10}
for !app.should_close() {
if app.key_down(.W) do player1.y += 5
if app.key_down(.S) do player1.y -= 5
player1.y = clamp(player1.y, 0, app.height() - player1.height)
if app.key_down(.Up) do player2.y += 5
if app.key_down(.Down) do player2.y -= 5
player2.y = clamp(player2.y, 0, app.height() - player2.height)
_ball := ball
_ball.x += ball_dir.x
if touching(_ball, player1) || touching(_ball, player2) do ball_dir.x = -ball_dir.x
else if ball.x < 0 || ball.x+ball.width >= app.width() {
player1.x = 0
player1.y = (app.height()-150)/2
player2.x = app.width()-30
player2.y = (app.height()-150)/2
ball.x = (app.width()-30)/2
ball.y = (app.width()-30)/2
}
_ball = ball
_ball.y += ball_dir.y
if touching(_ball, player1) || touching(_ball, player2) || ball.y < 0 || ball.y+ball.height >= app.height() do ball_dir.y = -ball_dir.y
ball.x += ball_dir.x
ball.y += ball_dir.y
slice.fill(bitmap, 0)
draw_rectangle(bitmap, 0xFFFFFF, player1)
draw_rectangle(bitmap, 0xFFFFFF, player2)
draw_rectangle(bitmap, 0xFFFFFF, ball)
app.render(bitmap)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment