drag puzzle
There is no browser build of this example yet.
Is it the same as C?
- Desktop:verified The Zig version renders a final frame that is byte-identical to the C example.
- Browser:verified In headless Chrome, the Zig version gives a final frame byte-identical to the C example built for the web.
//! raylib [shapes] example - drag puzzle
//!
//! Zig-API translation of examples/shapes/shapes_drag_puzzle.c (raylib 6.2).
//! Example licensed under an unmodified zlib/libpng license.
//! Copyright (c) 2026 Gabriel Piangers (@gabriel-piangers)
const rl = @import("raylib");
const rm = rl.math;
const Circle = struct {
center: rl.Vector2,
radius: f32,
};
const Triangle = struct {
v1: rl.Vector2,
v2: rl.Vector2,
v3: rl.Vector2,
fn contains(tri: Triangle, point: rl.Vector2) bool {
return rl.checkCollisionPointTriangle(point, tri.v1, tri.v2, tri.v3);
}
};
pub fn main() void {
// Initialization
const screen_width = 800;
const screen_height = 450;
rl.initWindow(screen_width, screen_height, "raylib [shapes] example - drag puzzle");
defer rl.closeWindow(); // Close window and OpenGL context
// Rectangle
var rec: rl.Rectangle = .{ .x = screen_width / 2 - 250, .y = screen_height / 2 + 50, .width = 100.0, .height = 100.0 };
const rec_area: rl.Rectangle = .{ .x = screen_width / 2 - 60, .y = screen_height / 2 - 110, .width = 110.0, .height = 110.0 };
var rec_picked_up = false;
// Circle
var circ: Circle = .{ .center = .{ .x = screen_width / 2, .y = screen_height / 2 + 100 }, .radius = 50.0 };
const circ_area: Circle = .{ .center = .{ .x = screen_width / 2 - 195, .y = screen_height / 2 - 55 }, .radius = 55.0 };
var circ_picked_up = false;
// Triangle
var tri: Triangle = .{ .v1 = .{ .x = 600, .y = 282 }, .v2 = .{ .x = 550, .y = 369 }, .v3 = .{ .x = 650, .y = 369 } };
const tri_area: Triangle = .{ .v1 = .{ .x = 600, .y = 115 }, .v2 = .{ .x = 540, .y = 222 }, .v3 = .{ .x = 660, .y = 222 } };
var tri_picked_up = false;
var mouse_offset: rl.Vector2 = .{ .x = 0.0, .y = 0.0 }; // Offset of the mouse relative to the object's pivot point
rl.setTargetFPS(60);
// Main game loop
while (!rl.windowShouldClose()) { // Detect window close button or ESC key
// Update
const mouse_position = rl.getMousePosition();
// Detect object pickup input
if (rl.isMouseButtonPressed(.left)) {
if (rl.checkCollisionPointRec(mouse_position, rec)) {
rec_picked_up = true;
mouse_offset = .{ .x = rec.x - mouse_position.x, .y = rec.y - mouse_position.y };
} else if (rl.checkCollisionPointCircle(mouse_position, circ.center, circ.radius)) {
circ_picked_up = true;
mouse_offset = .{ .x = circ.center.x - mouse_position.x, .y = circ.center.y - mouse_position.y };
} else if (tri.contains(mouse_position)) {
tri_picked_up = true;
mouse_offset = .{ .x = tri.v1.x - mouse_position.x, .y = tri.v1.y - mouse_position.y }; // Uses v1 as the pivot point
}
}
// Detect object drop input
if (rl.isMouseButtonReleased(.left)) {
if (rec_picked_up) {
rec_picked_up = false;
} else if (circ_picked_up) {
circ_picked_up = false;
} else if (tri_picked_up) {
tri_picked_up = false;
}
}
// Rectangle update
if (rec_picked_up) {
rec.x = mouse_position.x + mouse_offset.x;
rec.y = mouse_position.y + mouse_offset.y;
}
const rec_col = rl.getCollisionRec(rec_area, rec);
const rec_placed = rec_col.width == rec.width and rec_col.height == rec.height;
// Circle update
if (circ_picked_up) {
circ.center.x = mouse_position.x + mouse_offset.x;
circ.center.y = mouse_position.y + mouse_offset.y;
}
const circ_placed = rm.vector2Distance(circ.center, circ_area.center) < circ_area.radius - circ.radius;
// Triangle update
if (tri_picked_up) {
const v2_offset: rl.Vector2 = .{ .x = tri.v2.x - tri.v1.x, .y = tri.v2.y - tri.v1.y };
const v3_offset: rl.Vector2 = .{ .x = tri.v3.x - tri.v1.x, .y = tri.v3.y - tri.v1.y };
tri.v1 = .{ .x = mouse_position.x + mouse_offset.x, .y = mouse_position.y + mouse_offset.y };
tri.v2 = .{ .x = tri.v1.x + v2_offset.x, .y = tri.v1.y + v2_offset.y };
tri.v3 = .{ .x = tri.v1.x + v3_offset.x, .y = tri.v1.y + v3_offset.y };
}
// NOTE: upstream checks v2 twice (and never v3); kept for identical behavior
const tri_placed = tri_area.contains(tri.v1) and tri_area.contains(tri.v2) and tri_area.contains(tri.v2);
// Draw
rl.beginDrawing();
defer rl.endDrawing();
rl.clearBackground(.ray_white);
rl.drawRectangleLinesEx(rec_area, 2.0, if (rec_placed) .green else .red);
rl.drawCircleLinesEx(circ_area.center, circ_area.radius, 2.0, if (circ_placed) .green else .red);
rl.drawTriangleLinesEx(tri_area.v1, tri_area.v2, tri_area.v3, 2.0, if (tri_placed) .green else .red);
// Draws objects that are not picked up first
if (!tri_picked_up) rl.drawTriangle(tri.v1, tri.v2, tri.v3, .violet);
if (!circ_picked_up) rl.drawCircleV(circ.center, circ.radius, .blue);
if (!rec_picked_up) rl.drawRectangleRec(rec, .orange);
// Draws the object that is being dragged on top of others
if (tri_picked_up) rl.drawTriangle(tri.v1, tri.v2, tri.v3, .violet);
if (circ_picked_up) rl.drawCircleV(circ.center, circ.radius, .blue);
if (rec_picked_up) rl.drawRectangleRec(rec, .orange);
rl.drawText("Use mouse to drag and drop the objects into the right spot!", 10, 10, 20, .gray);
}
}/*******************************************************************************************
*
* raylib [shapes] example - drag puzzle
*
* Example complexity rating: [★☆☆☆] 1/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Gabriel Piangers (@gabriel-piangers) and reviewed by Ramon Santamaria (@raysan5)
*
* 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) 2026 Gabriel Piangers (@gabriel-piangers)
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
typedef struct {
Vector2 center;
float radius;
} Circle;
typedef struct {
Vector2 v1;
Vector2 v2;
Vector2 v3;
} Triangle;
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - drag puzzle");
// Rectangle
Rectangle rec = { screenWidth/2 - 250, screenHeight/2 + 50, 100.0f, 100.0f };
Rectangle recArea = { screenWidth/2 - 60, screenHeight/2 - 110, 110.0f, 110.0f };
bool recPickedUp = false;
// Circle
Circle circ = { screenWidth/2, screenHeight/2 + 100, 50.0f };
Circle circArea = { screenWidth/2 - 195, screenHeight/2 - 55, 55.0f };
bool circPickedUp = false;
// Triangle
Triangle tri = { { 600, 282 }, { 550, 369 }, { 650, 369 } };
Triangle triArea = { { 600, 115 }, { 540, 222 }, { 660, 222 } };
bool triPickedUp = false;
Vector2 mouseOffset = { 0.0f, 0.0f }; // Stores the offset of the mouse relative to the object's pivot point
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
Vector2 mousePosition = GetMousePosition();
bool recPlaced = false;
bool circPlaced = false;
bool triPlaced = false;
// Detect object pickup input
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && CheckCollisionPointRec(mousePosition, rec))
{
recPickedUp = true;
mouseOffset = (Vector2) { rec.x - mousePosition.x, rec.y - mousePosition.y };
}
else if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && CheckCollisionPointCircle(mousePosition, circ.center, circ.radius))
{
circPickedUp = true;
mouseOffset = (Vector2) { circ.center.x - mousePosition.x, circ.center.y - mousePosition.y };
}
else if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && CheckCollisionPointTriangle(mousePosition, tri.v1, tri.v2, tri.v3))
{
triPickedUp = true;
mouseOffset = (Vector2) { tri.v1.x - mousePosition.x, tri.v1.y - mousePosition.y }; // Uses v1 as the pivot point
}
// Detect object drop input
if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT) && recPickedUp) recPickedUp = false;
else if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT) && circPickedUp) circPickedUp = false;
else if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT) && triPickedUp) triPickedUp = false;
// Rectangle update
if (recPickedUp)
{
rec.x = mousePosition.x + mouseOffset.x;
rec.y = mousePosition.y + mouseOffset.y;
}
Rectangle RecCol = GetCollisionRec(recArea, rec);
if (RecCol.width == rec.width && RecCol.height == rec.height) recPlaced = true;
// Circle update
if (circPickedUp)
{
circ.center.x = mousePosition.x + mouseOffset.x;
circ.center.y = mousePosition.y + mouseOffset.y;
}
if (Vector2Distance(circ.center, circArea.center) < circArea.radius - circ.radius) circPlaced = true;
// Triangle update
if (triPickedUp)
{
Vector2 v2Offset = { tri.v2.x - tri.v1.x, tri.v2.y - tri.v1.y };
Vector2 v3Offset = { tri.v3.x - tri.v1.x, tri.v3.y - tri.v1.y };
tri.v1 = (Vector2) { mousePosition.x + mouseOffset.x, mousePosition.y + mouseOffset.y };
tri.v2 = (Vector2) { tri.v1.x + v2Offset.x, tri.v1.y + v2Offset.y };
tri.v3 = (Vector2) { tri.v1.x + v3Offset.x, tri.v1.y + v3Offset.y };
}
if (
CheckCollisionPointTriangle(tri.v1, triArea.v1, triArea.v2, triArea.v3) &&
CheckCollisionPointTriangle(tri.v2, triArea.v1, triArea.v2, triArea.v3) &&
CheckCollisionPointTriangle(tri.v2, triArea.v1, triArea.v2, triArea.v3)
) triPlaced = true;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
DrawRectangleLinesEx(recArea, 2.0f, recPlaced ? GREEN : RED);
DrawCircleLinesEx(circArea.center, circArea.radius, 2.0f, circPlaced ? GREEN : RED);
DrawTriangleLinesEx(triArea.v1, triArea.v2, triArea.v3, 2.0f, triPlaced ? GREEN : RED);
// Draws objects that are not picked up first
if (!triPickedUp) DrawTriangle(tri.v1, tri.v2, tri.v3, VIOLET);
if (!circPickedUp) DrawCircleV(circ.center, circ.radius, BLUE);
if (!recPickedUp) DrawRectangleRec(rec, ORANGE);
// Draws the object that is being dragged on top of others
if (triPickedUp) DrawTriangle(tri.v1, tri.v2, tri.v3, VIOLET);
if (circPickedUp) DrawCircleV(circ.center, circ.radius, BLUE);
if (recPickedUp) DrawRectangleRec(rec, ORANGE);
DrawText("Use mouse to drag and drop the objects into the right spot!", 10, 10, 20, GRAY);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}From raylib 6.2. The original on GitHub.