Selaa lähdekoodia

Gamepad axes and gamepad demo program

Karl Zylinski 6 kuukautta sitten
vanhempi
sitoutus
573c16e07a
6 muutettua tiedostoa jossa 144 lisäystä ja 9 poistoa
  1. 6 0
      README.md
  2. 60 0
      examples/gamepad/gamepad.odin
  3. 23 5
      karl2d.doc.odin
  4. 27 4
      karl2d.odin
  5. 1 0
      window_interface.odin
  6. 27 0
      window_win32.odin

+ 6 - 0
README.md

@@ -16,10 +16,16 @@ Might not be included:
 
 Here follows my near-future TODO list
 
+* should gamepad come from separate interface than window?
+	* keyboard input could also come from some input interface, but
+	  it is tightly bound to window in windows, so we'll see.
 * add more window flags
 * get_camera_view_matrix returns Mat4... should we go hardcore 2D and return Mat3 so you can do
     mat * Vec3{pos.x, pos.y, 1} for positions and mat * Vec3{dir.x, dir.y, 0} for dirs?
 * win32: Gamepad support
+	* axes
+	* check status of gamepad
+	* what happens when you pull one out?
 * basic text rendering
 * Do proper checks of vertex count and dispatch rendering when full
 	* What happens when list is full? We can't just empty the vertex list due to being used by input assembler etc.

+ 60 - 0
examples/gamepad/gamepad.odin

@@ -0,0 +1,60 @@
+package karl2d_gamepad_example
+
+import k2 "../.."
+import "core:log"
+
+Vec2 :: [2]f32
+
+button_color :: proc(button: k2.Gamepad_Button) -> k2.Color {
+	return k2.gamepad_button_is_held(0, button) ? k2.WHITE : k2.GRAY
+}
+
+main :: proc() {
+	context.logger = log.create_console_logger()
+	k2.init(500, 300, "Karl2D Gamepad Demo")
+	k2.set_window_position(300, 100)
+
+	for !k2.shutdown_wanted() {
+		k2.process_events()
+		k2.clear(k2.BLACK)
+
+		k2.draw_circle({120, 120}, 10, button_color(.Left_Face_Up))
+		k2.draw_circle({120, 160}, 10, button_color(.Left_Face_Down))
+		k2.draw_circle({100, 140}, 10, button_color(.Left_Face_Left))
+		k2.draw_circle({140, 140}, 10, button_color(.Left_Face_Right))
+
+		k2.draw_circle({320+50, 120}, 10, button_color(.Right_Face_Up))
+		k2.draw_circle({320+50, 160}, 10, button_color(.Right_Face_Down))
+		k2.draw_circle({300+50, 140}, 10, button_color(.Right_Face_Left))
+		k2.draw_circle({340+50, 140}, 10, button_color(.Right_Face_Right))
+
+		k2.draw_rect_vec({250 - 30, 140}, {20, 10}, button_color(.Middle_Face_Left))
+		k2.draw_rect_vec({250 + 10, 140}, {20, 10}, button_color(.Middle_Face_Right))
+
+		left_stick := Vec2 {
+			k2.get_gamepad_axis(0, .Left_Stick_X),
+			k2.get_gamepad_axis(0, .Left_Stick_Y),
+		}
+
+		right_stick := Vec2 {
+			k2.get_gamepad_axis(0, .Right_Stick_X),
+			k2.get_gamepad_axis(0, .Right_Stick_Y),
+		}
+
+		left_trigger  := k2.get_gamepad_axis(0, .Left_Trigger)
+		right_trigger := k2.get_gamepad_axis(0, .Right_Trigger)
+
+		k2.draw_rect_vec({80, 50}, {20, 10}, button_color(.Left_Shoulder))
+		k2.draw_rect_vec({50, 50} + {0, left_trigger * 20}, {20, 10}, k2.WHITE)
+
+		k2.draw_rect_vec({420, 50}, {20, 10}, button_color(.Right_Shoulder))
+		k2.draw_rect_vec({450, 50} + {0, right_trigger * 20}, {20, 10}, k2.WHITE)
+		k2.draw_circle({200, 200} + 20 * left_stick, 20, k2.gamepad_button_is_held(0, .Left_Stick_Press) ? k2.RED : k2.WHITE)
+		k2.draw_circle({300, 200} + 20 * right_stick, 20, k2.gamepad_button_is_held(0, .Right_Stick_Press) ? k2.RED : k2.WHITE)
+
+		k2.present()
+		free_all(context.temp_allocator)
+	}
+
+	k2.shutdown()
+}

+ 23 - 5
karl2d.doc.odin

@@ -93,17 +93,21 @@ get_mouse_wheel_delta :: proc() -> f32
 
 get_mouse_position :: proc() -> Vec2
 
-gamepad_button_went_down :: proc(gamepad: int, button: Gamepad_Button) -> bool
+gamepad_button_went_down :: proc(gamepad: Gamepad_Index, button: Gamepad_Button) -> bool
 
-gamepad_button_went_up :: proc(gamepad: int, button: Gamepad_Button) -> bool
+gamepad_button_went_up :: proc(gamepad: Gamepad_Index, button: Gamepad_Button) -> bool
 
-gamepad_button_is_held :: proc(gamepad: int, button: Gamepad_Button) -> bool
+gamepad_button_is_held :: proc(gamepad: Gamepad_Index, button: Gamepad_Button) -> bool
+
+get_gamepad_axis :: proc(gamepad: Gamepad_Index, axis: Gamepad_Axis) -> f32
 
 //---------//
 // DRAWING //
 //---------//
 draw_rect :: proc(r: Rect, c: Color)
 
+draw_rect_vec :: proc(pos: Vec2, size: Vec2, c: Color)
+
 draw_rect_ex :: proc(r: Rect, origin: Vec2, rot: f32, c: Color)
 
 draw_rect_outline :: proc(r: Rect, thickness: f32, color: Color)
@@ -191,6 +195,8 @@ Color :: [4]u8
 
 WHITE :: Color { 255, 255, 255, 255 }
 BLACK :: Color { 0, 0, 0, 255 }
+GRAY  :: Color { 127, 127, 127, 255 }
+RED   :: Color { 198, 80, 90, 255 }
 BLANK :: Color { 0, 0, 0, 0 }
 BLUE  :: Color { 30, 116, 240, 255 }
 
@@ -315,8 +321,6 @@ Handle :: hm.Handle
 Texture_Handle :: distinct Handle
 TEXTURE_NONE :: Texture_Handle {}
 
-MAX_GAMEPADS :: 4
-
 // This keeps track of the internal state of the library. Usually, you do not need to poke at it.
 // It is created and kept as a global variable when 'init' is called. However, 'init' also returns
 // the pointer to it, so you can later use 'set_internal_state' to restore it (after for example hot
@@ -494,6 +498,20 @@ Keyboard_Key :: enum {
 	NP_Equal        = 336,
 }
 
+MAX_GAMEPADS :: 4
+
+// A value between 0 and MAX_GAMEPADS - 1
+Gamepad_Index :: int
+
+Gamepad_Axis :: enum {
+	Left_Stick_X,
+	Left_Stick_Y,
+	Right_Stick_X,
+	Right_Stick_Y,
+	Left_Trigger,
+	Right_Trigger,
+}
+
 Gamepad_Button :: enum {
 	// DPAD buttons
 	Left_Face_Up,

+ 27 - 4
karl2d.odin

@@ -260,7 +260,7 @@ get_mouse_position :: proc() -> Vec2 {
 	return s.mouse_position
 }
 
-gamepad_button_went_down :: proc(gamepad: int, button: Gamepad_Button) -> bool {
+gamepad_button_went_down :: proc(gamepad: Gamepad_Index, button: Gamepad_Button) -> bool {
 	if gamepad < 0 || gamepad >= MAX_GAMEPADS {
 		return false
 	}
@@ -268,7 +268,7 @@ gamepad_button_went_down :: proc(gamepad: int, button: Gamepad_Button) -> bool {
 	return s.gamepad_button_went_down[gamepad][button]
 }
 
-gamepad_button_went_up :: proc(gamepad: int, button: Gamepad_Button) -> bool {
+gamepad_button_went_up :: proc(gamepad: Gamepad_Index, button: Gamepad_Button) -> bool {
 	if gamepad < 0 || gamepad >= MAX_GAMEPADS {
 		return false
 	}
@@ -276,7 +276,7 @@ gamepad_button_went_up :: proc(gamepad: int, button: Gamepad_Button) -> bool {
 	return s.gamepad_button_went_up[gamepad][button]
 }
 
-gamepad_button_is_held :: proc(gamepad: int, button: Gamepad_Button) -> bool {
+gamepad_button_is_held :: proc(gamepad: Gamepad_Index, button: Gamepad_Button) -> bool {
 	if gamepad < 0 || gamepad >= MAX_GAMEPADS {
 		return false
 	}
@@ -284,6 +284,10 @@ gamepad_button_is_held :: proc(gamepad: int, button: Gamepad_Button) -> bool {
 	return s.gamepad_button_is_held[gamepad][button]
 }
 
+get_gamepad_axis :: proc(gamepad: Gamepad_Index, axis: Gamepad_Axis) -> f32 {
+	return win.get_gamepad_axis(gamepad, axis)
+}
+
 //---------//
 // DRAWING //
 //---------//
@@ -303,6 +307,10 @@ draw_rect :: proc(r: Rect, c: Color) {
 	batch_vertex({r.x, r.y + r.h}, {0, 1}, c)
 }
 
+draw_rect_vec :: proc(pos: Vec2, size: Vec2, c: Color) {
+	draw_rect({pos.x, pos.y, size.x, size.y}, c)
+}
+
 draw_rect_ex :: proc(r: Rect, origin: Vec2, rot: f32, c: Color) {
 	if s.batch_texture != TEXTURE_NONE && s.batch_texture != s.shape_drawing_texture {
 		draw_current_batch()
@@ -843,6 +851,8 @@ Color :: [4]u8
 
 WHITE :: Color { 255, 255, 255, 255 }
 BLACK :: Color { 0, 0, 0, 255 }
+GRAY  :: Color { 127, 127, 127, 255 }
+RED   :: Color { 198, 80, 90, 255 }
 BLANK :: Color { 0, 0, 0, 0 }
 BLUE  :: Color { 30, 116, 240, 255 }
 
@@ -967,7 +977,6 @@ Handle :: hm.Handle
 Texture_Handle :: distinct Handle
 TEXTURE_NONE :: Texture_Handle {}
 
-MAX_GAMEPADS :: 4
 
 // This keeps track of the internal state of the library. Usually, you do not need to poke at it.
 // It is created and kept as a global variable when 'init' is called. However, 'init' also returns
@@ -1146,6 +1155,20 @@ Keyboard_Key :: enum {
 	NP_Equal        = 336,
 }
 
+MAX_GAMEPADS :: 4
+
+// A value between 0 and MAX_GAMEPADS - 1
+Gamepad_Index :: int
+
+Gamepad_Axis :: enum {
+	Left_Stick_X,
+	Left_Stick_Y,
+	Right_Stick_X,
+	Right_Stick_Y,
+	Left_Trigger,
+	Right_Trigger,
+}
+
 Gamepad_Button :: enum {
 	// DPAD buttons
 	Left_Face_Up,

+ 1 - 0
window_interface.odin

@@ -14,6 +14,7 @@ Window_Interface :: struct {
 	set_position: proc(x: int, y: int),
 	set_size: proc(w, h: int),
 	set_flags: proc(flags: Window_Flags),
+	get_gamepad_axis: proc(gamepad: int, axis: Gamepad_Axis) -> f32,
 
 	set_internal_state: proc(state: rawptr),
 }

+ 27 - 0
window_win32.odin

@@ -14,6 +14,7 @@ WINDOW_INTERFACE_WIN32 :: Window_Interface {
 	set_position = win32_set_position,
 	set_size = win32_set_size,
 	set_flags = win32_set_flags,
+	get_gamepad_axis = win32_get_gamepad_axis,
 	set_internal_state = win32_set_internal_state,
 }
 
@@ -181,6 +182,32 @@ win32_set_flags :: proc(flags: Window_Flags) {
 	win32.SetWindowLongW(s.hwnd, win32.GWL_STYLE, i32(style))
 }
 
+win32_get_gamepad_axis :: proc(gamepad: int, axis: Gamepad_Axis) -> f32 {
+	if gamepad < 0 || gamepad >= MAX_GAMEPADS {
+		return 0
+	}
+
+	gp_state: win32.XINPUT_STATE
+	if win32.XInputGetState(win32.XUSER(gamepad), &gp_state) == .SUCCESS {
+		gp := gp_state.Gamepad
+
+		// Numbers from https://learn.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_gamepad
+		STICK_MAX   :: 32767
+		TRIGGER_MAX :: 255
+
+		switch axis {
+		case .Left_Stick_X: return f32(gp.sThumbLX) / STICK_MAX
+		case .Left_Stick_Y: return -f32(gp.sThumbLY) / STICK_MAX
+		case .Right_Stick_X: return f32(gp.sThumbRX) / STICK_MAX
+		case .Right_Stick_Y: return -f32(gp.sThumbRY) / STICK_MAX
+		case .Left_Trigger: return f32(gp.bLeftTrigger) / TRIGGER_MAX
+		case .Right_Trigger: return f32(gp.bRightTrigger) / TRIGGER_MAX
+		}
+	}
+
+	return 0
+}
+
 win32_set_internal_state :: proc(state: rawptr) {
 	assert(state != nil)
 	s = (^Win32_State)(state)