-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvectors6.nim
More file actions
188 lines (159 loc) · 7.74 KB
/
vectors6.nim
File metadata and controls
188 lines (159 loc) · 7.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# ****************************************************************************************
#
# raylib [vectors] lesson 6 - Interactivity and State
#
# This lesson demonstrates:
# - Handling user input to trigger actions.
# - Managing the state of dynamic objects (e.g., a bullet).
# - Calculating directional vectors for movement.
# - Basic collision detection between a point and a polygon.
#
# ****************************************************************************************
import raylib
import raymath
import math
const
screenWidth = 800
screenHeight = 450
proc main =
initWindow(screenWidth, screenHeight, "raylib [vectors] lesson 6 - Pew Pew!")
setTargetFPS(60)
# Define the screen boundaries as a rectangle for collision checks.
let screenBounds = Rectangle(x: 0, y: 0, width: screenWidth, height: screenHeight)
# --- Model Space Definitions ---
let triangleVertices = [
Vector2(x: 0.0, y: -30.0), # Tip of the "ship"
Vector2(x: -25.0, y: 30.0),
Vector2(x: 25.0, y: 30.0)
]
let quadVertices = [
Vector2(x: -25.0, y: -25.0),
Vector2(x: 25.0, y: -25.0),
Vector2(x: 25.0, y: 25.0),
Vector2(x: -25.0, y: 25.0)
]
# --- World Space Positions ---
let triangleBasePos = Vector2(x: screenWidth * 0.25, y: screenHeight / 2.0)
let quadWorldPos = Vector2(x: screenWidth * 0.75, y: screenHeight / 2.0)
# --- Game State Variables ---
var time: float32 = 0.0
# These variables manage the "hit" feedback for the quad.
var quadColor = DarkBlue
# When the quad is hit, this cooldown is set. It counts down each frame.
var quadHitCooldown: float32 = 0.0
const hitDisplayTime: float32 = 0.5 # How long the quad stays red (in seconds)
# LESSON 1: BULLET STATE
# We need variables to track the bullet's state: its activity, position, and velocity.
var bulletActive = false
var bulletPos: Vector2
var bulletVelocity: Vector2
# The 'f32 suffix is a shorthand to create a float32 literal.
# Nim's type inference automatically makes `bulletSpeed` a float32,
# so we don't need to write `const bulletSpeed: float32 = ...`.
const bulletSpeed = 400.0'f32
# Main game loop
# --------------------------------------------------------------------------------------
while not windowShouldClose():
# Update
# ----------------------------------------------------------------------------------
let dt = getFrameTime() # Get the time elapsed since the last frame.
time += dt
# Manage the quad's hit-flash effect.
if quadHitCooldown > 0:
quadHitCooldown -= dt
# When the cooldown finishes, reset the color from Red back to DarkBlue.
elif quadColor == Red:
quadColor = DarkBlue
# --- Triangle Transformation ---
let triRotation = time * 50.0
let movementFactor = (sin(time) + 1.0) / 2.0
let startPos = triangleBasePos
let endPos = Vector2(x: 100.0, y: screenHeight - 100.0)
# "lerp" stands for Linear Interpolation. It finds a point on the line
# between startPos and endPos. The movementFactor (0.0 to 1.0) determines
# how far along that line the point is, creating smooth back-and-forth movement.
let triangleWorldPos = lerp(startPos, endPos, movementFactor)
let rotationMatrix: Matrix = rotateZ(triRotation.degToRad)
# --- Quad Transformation ---
let scaleFactor = 1.0 + sin(time * 2.0) * 0.4
let scalingMatrix: Matrix = scale(scaleFactor, scaleFactor, 1.0)
let quadTranslationMatrix: Matrix = translate(quadWorldPos.x, quadWorldPos.y, 0.0)
# remember: to scale then translate, we multiply in reverse order
let quadModelMatrix: Matrix = multiply(quadTranslationMatrix, scalingMatrix)
# right-to-left <-
# --- Vertex Transformations (using loops for clarity) ---
# For the triangle, we use a "hybrid" method: rotate with a matrix,
# then translate with vector addition.
var transformedTriVertices: array[3, Vector2]
# In Python, we would would say 'for i, v in enumerate(triangleVertices):'
# In Nim, the 'enumerate' is implicit because we are using 2 variables to iterate
for i, v in triangleVertices:
transformedTriVertices[i] = transform(v, rotationMatrix) + triangleWorldPos
# For the quad, we use the full model matrix to transform each vertex.
var transformedQuadVertices: array[4, Vector2]
for i, v in quadVertices:
transformedQuadVertices[i] = transform(v, quadModelMatrix)
# LESSON 2: INPUT HANDLING AND FIRING
# Check if the space key is pressed and if there isn't already an active bullet.
if isKeyPressed(Space) and not bulletActive:
bulletActive = true
# The bullet starts at the tip of the triangle. The tip is the first vertex,
# so we use its transformed position.
bulletPos = transformedTriVertices[0]
# To get the direction, we start with a "forward" vector in model space.
# Since the triangle's tip points up (negative Y), our forward is (0, -1).
let forwardVector = Vector2(x: 0.0, y: -1.0)
# We then rotate this vector by the triangle's current rotation to get the
# final direction in world space. This gives us a normalized direction vector.
let direction = rotate(forwardVector, triRotation.degToRad)
# The velocity is the direction multiplied by the speed.
bulletVelocity = direction * bulletSpeed
# LESSON 3: BULLET UPDATE AND COLLISION
if bulletActive:
# Update bullet position based on its velocity and the frame time.
bulletPos += bulletVelocity * dt
# Deactivate bullet if it goes off-screen. A convenient shorthand for this
# is to use raylib's built-in function to check if the point is no longer
# inside the screen's rectangle.
if not checkCollisionPointRec(bulletPos, screenBounds):
bulletActive = false
# Check for collision between the bullet's position and the quad's vertices.
# `checkCollisionPointPoly` is a C-style function that expects a pointer to the
# start of the vertex array. Nim's `[]` slice operator on an array provides
# this pointer automatically.
if checkCollisionPointPoly(bulletPos, transformedQuadVertices):
bulletActive = false
quadColor = Red # Change color on hit!
quadHitCooldown = hitDisplayTime # Start the cooldown timer.
# Draw
# ------------------------------------------------------------------------------------
beginDrawing()
clearBackground(RayWhite)
# --- Draw UI ---
drawText("Press [Space] to fire!", 10, 10, 20, LightGray)
# --- Draw Shapes ---
drawTriangleLines(
transformedTriVertices[0], transformedTriVertices[1], transformedTriVertices[2], Maroon)
for i in 0 ..< transformedQuadVertices.len:
drawLine(
transformedQuadVertices[i], transformedQuadVertices[(i + 1) mod 4], 2.0, quadColor)
# LESSON 4: DRAWING THE BULLET
# Only draw the bullet if it's active.
if bulletActive:
drawCircle(bulletPos, 5.0, Black)
endDrawing()
# ------------------------------------------------------------------------------------
# De-Initialization
# --------------------------------------------------------------------------------------
closeWindow()
# --------------------------------------------------------------------------------------
main()
#[ Think of it like a "connect-the-dots" puzzle.
Your original shape (quadVertices) is the set of numbered dots on the page
in their starting positions.
The transformation matrix (quadModelMatrix) is a set of instructions
that tells you where to move each individual dot.
The drawing functions (drawLine in your case) are you, with a pencil,
drawing straight lines between the new positions of the dots.
The matrix itself has no concept of a "line";
it only knows how to take an input point (x, y) and output a new point (x', y'). ]#