diff --git a/public/resources/guides/graphics/basics/BasicGraphicsResources.zip b/public/resources/guides/graphics/basics/BasicGraphicsResources.zip new file mode 100644 index 000000000..c6565fdd9 Binary files /dev/null and b/public/resources/guides/graphics/basics/BasicGraphicsResources.zip differ diff --git a/public/resources/guides/graphics/basics/BeginnerGameTutorialCode.zip b/public/resources/guides/graphics/basics/BeginnerGameTutorialCode.zip new file mode 100644 index 000000000..ac028666f Binary files /dev/null and b/public/resources/guides/graphics/basics/BeginnerGameTutorialCode.zip differ diff --git a/public/usage-examples/color/saturation_of-1-example-oop.cs b/public/usage-examples/color/saturation_of-1-example-oop.cs new file mode 100644 index 000000000..2e80a1c1c --- /dev/null +++ b/public/usage-examples/color/saturation_of-1-example-oop.cs @@ -0,0 +1,27 @@ +using SplashKitSDK; + +namespace SaturationOfExample +{ + public class Program + { + public static void Main() + { + SplashKit.OpenWindow("Saturation Of", 800, 600); + + Color color = SplashKit.RandomRGBColor(255); + // Function used here ↓ + double colorSaturation = Math.Round(SplashKit.SaturationOf(color), 6); + Rectangle rectangle = SplashKit.RectangleFrom(200, 100, 400, 300); + + SplashKit.ClearScreen(Color.White); + SplashKit.FillRectangle(color, rectangle); + SplashKit.DrawText("This color's saturation is " + colorSaturation.ToString(), Color.Black, 235, 450); + SplashKit.DrawText("It's RGBA values are: R-" + SplashKit.RedOf(color) + ", G-" + SplashKit.GreenOf(color) + ", B-" + SplashKit.BlueOf(color) + ", A-" + SplashKit.AlphaOf(color), Color.Black, 235, 470); + SplashKit.RefreshScreen(); + + SplashKit.Delay(5000); + + SplashKit.CloseAllWindows(); + } + } +} \ No newline at end of file diff --git a/public/usage-examples/color/saturation_of-1-example-top-level.cs b/public/usage-examples/color/saturation_of-1-example-top-level.cs new file mode 100644 index 000000000..46f9c8dd9 --- /dev/null +++ b/public/usage-examples/color/saturation_of-1-example-top-level.cs @@ -0,0 +1,19 @@ +using SplashKitSDK; +using static SplashKitSDK.SplashKit; + +OpenWindow("Saturation Of", 800, 600); + +Color color = RandomRGBColor(255); +// Function used here ↓ +double colorSaturation = Math.Round(SaturationOf(color), 6); +Rectangle rectangle = RectangleFrom(200, 100, 400, 300); + +ClearScreen(ColorWhite()); +FillRectangle(color, rectangle); +DrawText("This color's saturation is " + colorSaturation.ToString(), ColorBlack(), 235, 450); +DrawText("It's RGBA values are: R-" + RedOf(color) + ", G-" + GreenOf(color) + ", B-" + BlueOf(color) + ", A-" + AlphaOf(color), ColorBlack(), 235, 470); +RefreshScreen(); + +Delay(5000); + +CloseAllWindows(); \ No newline at end of file diff --git a/public/usage-examples/color/saturation_of-1-example.cpp b/public/usage-examples/color/saturation_of-1-example.cpp new file mode 100644 index 000000000..000797215 --- /dev/null +++ b/public/usage-examples/color/saturation_of-1-example.cpp @@ -0,0 +1,22 @@ +#include "splashkit.h" + +int main() +{ + open_window("Saturation Of", 800, 600); + + color color = random_rgb_color(255); + // Function used here ↓ + double color_saturation = saturation_of(color); + rectangle rectangle = rectangle_from(200, 100, 400, 300); + + clear_screen(color_white()); + fill_rectangle(color, rectangle); + draw_text("This color's saturation is " + std::to_string(color_saturation), color_black(), 235, 450); + draw_text("It's RGBA values are: R-" + std::to_string(red_of(color)) + ", G-" + std::to_string(green_of(color)) + ", B-" + std::to_string(blue_of(color))+ ", A-" + std::to_string(alpha_of(color)), color_black(), 235, 470); + refresh_screen(); + + delay(5000); + + close_all_windows(); + return 0; +} \ No newline at end of file diff --git a/public/usage-examples/color/saturation_of-1-example.png b/public/usage-examples/color/saturation_of-1-example.png new file mode 100644 index 000000000..e0509bde4 Binary files /dev/null and b/public/usage-examples/color/saturation_of-1-example.png differ diff --git a/public/usage-examples/color/saturation_of-1-example.py b/public/usage-examples/color/saturation_of-1-example.py new file mode 100644 index 000000000..3e92acc4d --- /dev/null +++ b/public/usage-examples/color/saturation_of-1-example.py @@ -0,0 +1,18 @@ +from splashkit import * + +open_window("Saturation Of", 800, 600) + +color = random_rgb_color(255) +# Function used here ↓ +color_saturation = round(saturation_of(color), 6) +rectangle = rectangle_from(200, 100, 400, 300) + +clear_screen(color_white()) +fill_rectangle_record(color, rectangle) +draw_text_no_font_no_size("This color's saturation is " + str(color_saturation), color_black(), 235, 450) +draw_text_no_font_no_size("It's RGBA values are: R-" + str(red_of(color)) + ", G-" + str(green_of(color)) + ", B-" + str(blue_of(color)) + ", A-" + str(alpha_of(color)), color_black(), 235, 470) +refresh_screen() + +delay(5000) + +close_all_windows() \ No newline at end of file diff --git a/public/usage-examples/color/saturation_of-1-example.txt b/public/usage-examples/color/saturation_of-1-example.txt new file mode 100644 index 000000000..fb5cb8023 --- /dev/null +++ b/public/usage-examples/color/saturation_of-1-example.txt @@ -0,0 +1 @@ +Draws a rectangle in a random colour. The colour's saturation value is displayed on screen \ No newline at end of file diff --git a/public/usage-examples/generative_ai/generate-convo-1-example-oop.cs b/public/usage-examples/generative_ai/generate-convo-1-example-oop.cs new file mode 100644 index 000000000..93b623ca5 --- /dev/null +++ b/public/usage-examples/generative_ai/generate-convo-1-example-oop.cs @@ -0,0 +1,68 @@ +// Simple Terminal AI Conversation - OOP C# Example +// This example uses a class structure + +using SplashKitSDK; + +class AIConversation +{ + // Main entry point + static void Main() + { + // Create an instance of the conversation class + AIConversation conversation = new AIConversation(); + + // Run the conversation + conversation.Start(); + } + + // Method to start the conversation loop + public void Start() + { + // Display welcome message + SplashKit.ClearScreen(); + SplashKit.WriteLine("=== Terminal AI Conversation ==="); + SplashKit.WriteLine("Type your message and press Enter to chat with AI"); + SplashKit.WriteLine("Type 'quit' to exit"); + SplashKit.WriteLine(""); + + // Continue conversation until user quits + while (true) + { + string userMessage = GetUserInput(); + + // Check if user wants to quit + if (userMessage == "quit") + { + SplashKit.WriteLine("Goodbye!"); + break; + } + + // Skip empty messages + if (string.IsNullOrWhiteSpace(userMessage)) + { + continue; + } + + // Get and display AI response + DisplayAIResponse(userMessage); + } + } + + // Method to get user input + private string GetUserInput() + { + SplashKit.Write("You: "); + return SplashKit.ReadLine(); + } + + // Method to generate and display AI response + private void DisplayAIResponse(string userMessage) + { + // Generate AI response using generate_text() + string aiResponse = SplashKit.GenerateText(userMessage); + + // Display the response + SplashKit.WriteLine("AI: " + aiResponse); + SplashKit.WriteLine(""); + } +} diff --git a/public/usage-examples/generative_ai/generate-convo-1-example-top-level.cs b/public/usage-examples/generative_ai/generate-convo-1-example-top-level.cs new file mode 100644 index 000000000..f3d430ebc --- /dev/null +++ b/public/usage-examples/generative_ai/generate-convo-1-example-top-level.cs @@ -0,0 +1,41 @@ +// Simple Terminal AI Conversation - Top-Level C# Example +// This example uses top-level statements (C# 9+) + +using SplashKitSDK; + +// Display welcome message +SplashKit.ClearScreen(); +SplashKit.WriteLine("=== Terminal AI Conversation ==="); +SplashKit.WriteLine("Type your message and press Enter to chat with AI"); +SplashKit.WriteLine("Type 'quit' to exit"); +SplashKit.WriteLine(""); + +string userMessage; + +// Simple conversation loop +while (true) +{ + // Get user input + SplashKit.Write("You: "); + userMessage = SplashKit.ReadLine(); + + // Check if user wants to quit + if (userMessage == "quit") + { + SplashKit.WriteLine("Goodbye!"); + break; + } + + // Skip empty messages + if (string.IsNullOrWhiteSpace(userMessage)) + { + continue; + } + + // Generate AI response using generate_text() + string aiResponse = SplashKit.GenerateText(userMessage); + + // Display AI response + SplashKit.WriteLine("AI: " + aiResponse); + SplashKit.WriteLine(""); +} diff --git a/public/usage-examples/generative_ai/generate-convo-1-example.cpp b/public/usage-examples/generative_ai/generate-convo-1-example.cpp new file mode 100644 index 000000000..1b9c1211c --- /dev/null +++ b/public/usage-examples/generative_ai/generate-convo-1-example.cpp @@ -0,0 +1,48 @@ +#include "splashkit.h" +#include +#include + +int main() +{ + // Initialize SplashKit + open_window("AI Conversation Example", 800, 600); + + // Display welcome message + write_line("=== Terminal AI Conversation ==="); + write_line("Type your message and press Enter to chat with AI"); + write_line("Type 'quit' to exit"); + write_line(""); + + std::string user_message; + + // Simple conversation loop + while (true) + { + // Get user input + write("You: "); + user_message = read_line(); + + // Check if user wants to quit + if (user_message == "quit") + { + write_line("Goodbye!"); + break; + } + + // Skip empty messages + if (user_message.empty()) + { + continue; + } + + // Generate AI response using generate_text() + std::string ai_response = generate_text(user_message); + + // Display AI response + write_line("AI: " + ai_response); + write_line(""); + } + + close_window("AI Conversation Example"); + return 0; +} diff --git a/public/usage-examples/generative_ai/generate-convo-1-example.py b/public/usage-examples/generative_ai/generate-convo-1-example.py new file mode 100644 index 000000000..a99cf5584 --- /dev/null +++ b/public/usage-examples/generative_ai/generate-convo-1-example.py @@ -0,0 +1,38 @@ +# Simple Terminal AI Conversation - Python Example + +from splashkit import * + +def main(): + """Main function to run the AI conversation""" + + # Display welcome message + clear_screen() + print("=== Terminal AI Conversation ===") + print("Type your message and press Enter to chat with AI") + print("Type 'quit' to exit") + print("") + + # Simple conversation loop + while True: + # Get user input + user_message = input("You: ") + + # Check if user wants to quit + if user_message.lower() == "quit": + print("Goodbye!") + break + + # Skip empty messages + if not user_message.strip(): + continue + + # Generate AI response using generate_text() + ai_response = generate_text(user_message) + + # Display AI response + print(f"AI: {ai_response}") + print("") + +# Run the program +if __name__ == "__main__": + main() diff --git a/public/usage-examples/generative_ai/generate-convo-1-example.txt b/public/usage-examples/generative_ai/generate-convo-1-example.txt new file mode 100644 index 000000000..0e4766784 --- /dev/null +++ b/public/usage-examples/generative_ai/generate-convo-1-example.txt @@ -0,0 +1,72 @@ +Simple Terminal AI Conversation Example +======================================== + +Overview +-------- +This example demonstrates how to create a simple terminal-based AI conversation +using the generate_text() function from SplashKit. + +How It Works +----------- +1. The program displays a welcome message +2. User types a message and presses Enter +3. The message is sent to generate_text() +4. The AI response is printed in the terminal +5. The user can continue the conversation or type 'quit' to exit + +Key Concepts +----------- +- Input/Output: Reading user input and displaying responses +- generate_text(): The SplashKit function that generates AI responses +- Loops: Using a while loop to maintain the conversation +- String Handling: Working with text input and output + +Code Flow +--------- +Welcome Message + ↓ +Loop Start + ↓ +Get User Input + ↓ +Check for 'quit' + ↓ +Generate AI Response + ↓ +Display Response + ↓ +Loop Back (until user types 'quit') + +Example Usage +----------- +=== Terminal AI Conversation === +Type your message and press Enter to chat with AI +Type 'quit' to exit + +You: Hello, how are you? +AI: I'm doing well, thank you for asking! How can I assist you today? + +You: What is SplashKit? +AI: SplashKit is a cross-platform C++ library that makes it easy to learn + and teach programming through games and multimedia applications. + +You: quit +Goodbye! + +Files Included +-------------- +1. generate-convo-1-example.cpp - C++ version +2. generate-convo-1-example-top-level.cs - C# top-level statements version +3. generate-convo-1-example-oop.cs - C# object-oriented version +4. generate-convo-1-example.py - Python version +5. generate-convo-1-example.txt - This documentation file + +Learning Tips +------------- +- Start with the Python version if you're new to programming +- The top-level C# version is good for understanding basic structure +- The OOP versions show how to organize code into reusable methods +- Try modifying the program to add new features like: + * Saving conversation history + * Adding more elaborate welcome messages + * Creating a conversation history log diff --git a/public/usage-examples/geometry/rectangle_around-1-example-oop.cs b/public/usage-examples/geometry/rectangle_around-1-example-oop.cs new file mode 100644 index 000000000..91d32b902 --- /dev/null +++ b/public/usage-examples/geometry/rectangle_around-1-example-oop.cs @@ -0,0 +1,59 @@ +using SplashKitSDK; + +namespace RectangleAroundExample +{ + public class Program + { + public static void Main() + { + SplashKit.OpenWindow("Boring Screensaver", 800, 600); + + Circle circle; + int circleSize = 30; + float rotationDegrees = 0; + Point2D circleCoordinates; + bool growing = true; + SplashKitSDK.Timer mainTimer = SplashKit.CreateTimer("mainTimer"); + SplashKit.StartTimer(mainTimer); + SplashKitSDK.Timer reverseTimer = SplashKit.CreateTimer("reverseTimer"); + SplashKit.StartTimer(reverseTimer); + + while (!SplashKit.QuitRequested()) + { + rotationDegrees += 0.005f; + circleCoordinates = SplashKit.PointAt(300 + 150 * SplashKit.Cosine(rotationDegrees), 300 + 150 * SplashKit.Sine(rotationDegrees)); + circle = SplashKit.CircleAt(circleCoordinates, circleSize); + + if (SplashKit.TimerTicks(mainTimer) >= 40 && growing == true) + { + circleSize += 1; + SplashKit.ResetTimer(mainTimer); + } + else if (SplashKit.TimerTicks(reverseTimer) >= 3000) + { + growing = false; + } + + if (SplashKit.TimerTicks(mainTimer) >= 40 && growing == false) + { + circleSize -= 1; + SplashKit.ResetTimer(mainTimer); + } + else if (SplashKit.TimerTicks(reverseTimer) >= 6000) + { + growing = true; + SplashKit.ResetTimer(reverseTimer); + } + + SplashKit.ProcessEvents(); + + SplashKit.ClearScreen(Color.White); + // A rectangle is drawn which encompasses the circle. It shares the same height, width and position + SplashKit.DrawRectangle(Color.Black, SplashKit.RectangleAround(circle)); + SplashKit.FillCircle(Color.Red, circle); + SplashKit.RefreshScreen(); + } + SplashKit.CloseAllWindows(); + } + } +} \ No newline at end of file diff --git a/public/usage-examples/geometry/rectangle_around-1-example-top-level.cs b/public/usage-examples/geometry/rectangle_around-1-example-top-level.cs new file mode 100644 index 000000000..b2108bbcf --- /dev/null +++ b/public/usage-examples/geometry/rectangle_around-1-example-top-level.cs @@ -0,0 +1,52 @@ +using SplashKitSDK; +using static SplashKitSDK.SplashKit; + +OpenWindow("Boring Screensaver", 800, 600); + +Circle circle; +int circleSize = 30; +float rotationDegrees = 0; +Point2D circleCoordinates; +bool growing = true; +// SplashKitSDK.Timer needed to distinguish from System.Threading.Timer ↓ +SplashKitSDK.Timer mainTimer = CreateTimer("mainTimer"); +StartTimer(mainTimer); +SplashKitSDK.Timer reverseTimer = CreateTimer("reverseTimer"); +StartTimer(reverseTimer); + +while (!QuitRequested()) +{ + rotationDegrees += 0.005f; + circleCoordinates = PointAt(300 + 150 * Cosine(rotationDegrees), 300 + 150 * Sine(rotationDegrees)); + circle = CircleAt(circleCoordinates, circleSize); + + if (TimerTicks(mainTimer) >= 40 && growing == true) + { + circleSize += 1; + ResetTimer(mainTimer); + } + else if (TimerTicks(reverseTimer) >= 3000) + { + growing = false; + } + + if (TimerTicks(mainTimer) >= 40 && growing == false) + { + circleSize -= 1; + ResetTimer(mainTimer); + } + else if (TimerTicks(reverseTimer) >= 6000) + { + growing = true; + ResetTimer(reverseTimer); + } + + ProcessEvents(); + + ClearScreen(ColorWhite()); + // A rectangle is drawn which encompasses the circle. It shares the same height, width and position + DrawRectangle(ColorBlack(), RectangleAround(circle)); + FillCircle(ColorRed(), circle); + RefreshScreen(); +} +CloseAllWindows(); \ No newline at end of file diff --git a/public/usage-examples/geometry/rectangle_around-1-example.cpp b/public/usage-examples/geometry/rectangle_around-1-example.cpp new file mode 100644 index 000000000..36a9f78ab --- /dev/null +++ b/public/usage-examples/geometry/rectangle_around-1-example.cpp @@ -0,0 +1,54 @@ +#include "splashkit.h" + +int main() +{ + open_window("Boring Screensaver", 800, 600); + + circle circle; + int circle_size = 30; + float rotation_degrees = 0; + point_2d circle_coordinates; + bool growing = true; + timer main_timer = create_timer("main_timer"); + start_timer(main_timer); + timer reverse_timer = create_timer("reverse_timer"); + start_timer(reverse_timer); + + while (!quit_requested()) + { + rotation_degrees += 0.005; + circle_coordinates = point_at((300 + 150 * cosine(rotation_degrees)), (300 + 150 * sine(rotation_degrees))); + circle = circle_at(circle_coordinates, circle_size); + + if (timer_ticks(main_timer) >= 40 && growing == true) + { + circle_size += 1; + reset_timer(main_timer); + } + else if (timer_ticks(reverse_timer) >= 3000) + { + growing = false; + } + + if (timer_ticks(main_timer) >= 40 && growing == false) + { + circle_size -= 1; + reset_timer(main_timer); + } + else if (timer_ticks(reverse_timer) >= 6000) + { + growing = true; + reset_timer(reverse_timer); + } + + process_events(); + + clear_screen(); + // A rectangle is drawn which encompasses the circle. It shares the same height, width and position + draw_rectangle(color_black(), rectangle_around(circle)); + fill_circle(color_red(), circle); + refresh_screen(); + } + close_all_windows(); + return 0; +} \ No newline at end of file diff --git a/public/usage-examples/geometry/rectangle_around-1-example.gif b/public/usage-examples/geometry/rectangle_around-1-example.gif new file mode 100644 index 000000000..c88ed49e6 Binary files /dev/null and b/public/usage-examples/geometry/rectangle_around-1-example.gif differ diff --git a/public/usage-examples/geometry/rectangle_around-1-example.py b/public/usage-examples/geometry/rectangle_around-1-example.py new file mode 100644 index 000000000..be32d3f31 --- /dev/null +++ b/public/usage-examples/geometry/rectangle_around-1-example.py @@ -0,0 +1,41 @@ +from splashkit import * + +open_window("Boring Screensaver", 800, 600) + +circle = Circle +circle_size = 30 +rotation_degrees = 0 +circle_coordinates = 0 +growing = True +main_timer = create_timer("main_timer") +start_timer(main_timer) +reverse_timer = create_timer("reverse_timer") +start_timer(reverse_timer) + +while (not quit_requested()): + rotation_degrees = rotation_degrees + 0.005 + circle_coordinates = point_at((300 + 150 * cosine(rotation_degrees)), (300 + 150 * sine(rotation_degrees))) + circle = circle_at(circle_coordinates, circle_size) + + if timer_ticks(main_timer) >= 40 and growing == True: + circle_size += 1 + reset_timer(main_timer) + elif timer_ticks(reverse_timer) >= 3000: + growing = False + + if timer_ticks(main_timer) >= 40 and growing == False: + circle_size -= 1 + reset_timer(main_timer) + elif timer_ticks(reverse_timer) >= 6000: + growing = True + reset_timer(reverse_timer) + + process_events() + + clear_screen_to_white() + # A rectangle is drawn which encompasses the circle. It shares the same height, width and position + draw_rectangle_record(color_black(), rectangle_around_circle(circle)) + fill_circle_record(color_red(), circle) + refresh_screen() + +close_all_windows() \ No newline at end of file diff --git a/public/usage-examples/geometry/rectangle_around-1-example.txt b/public/usage-examples/geometry/rectangle_around-1-example.txt new file mode 100644 index 000000000..dc4c2fe6c --- /dev/null +++ b/public/usage-examples/geometry/rectangle_around-1-example.txt @@ -0,0 +1 @@ +A perpetually moving circle which increases and decreases in size, surrounded by a rectangle shape \ No newline at end of file diff --git a/public/usage-examples/graphics/bitmap_center-1-example-oop.cs b/public/usage-examples/graphics/bitmap_center-1-example-oop.cs new file mode 100644 index 000000000..13e3ce1a9 --- /dev/null +++ b/public/usage-examples/graphics/bitmap_center-1-example-oop.cs @@ -0,0 +1,24 @@ +using SplashKitSDK; + +namespace BitmapCenterExample +{ + public class Program + { + public static void Main() + { + SplashKit.OpenWindow("Bitmap Center", 800, 600); + + Bitmap imageBitmap = SplashKit.LoadBitmap("image_bitmap", "image1.jpg"); + Point2D centerPoint = SplashKit.BitmapCenter(imageBitmap); + + SplashKit.ClearScreen(Color.White); + SplashKit.DrawBitmap(imageBitmap, 0, 0); + SplashKit.FillCircle(Color.Red, SplashKit.CircleAt(centerPoint, 5)); + SplashKit.RefreshScreen(); + + SplashKit.Delay(5000); + + SplashKit.CloseAllWindows(); + } + } +} \ No newline at end of file diff --git a/public/usage-examples/graphics/bitmap_center-1-example-resources.zip b/public/usage-examples/graphics/bitmap_center-1-example-resources.zip new file mode 100644 index 000000000..5369978d2 Binary files /dev/null and b/public/usage-examples/graphics/bitmap_center-1-example-resources.zip differ diff --git a/public/usage-examples/graphics/bitmap_center-1-example-top-level.cs b/public/usage-examples/graphics/bitmap_center-1-example-top-level.cs new file mode 100644 index 000000000..dec14381e --- /dev/null +++ b/public/usage-examples/graphics/bitmap_center-1-example-top-level.cs @@ -0,0 +1,16 @@ +using SplashKitSDK; +using static SplashKitSDK.SplashKit; + +OpenWindow("Bitmap Center", 800, 600); + +Bitmap imageBitmap = LoadBitmap("image_bitmap", "image1.jpg"); +Point2D centerPoint = BitmapCenter(imageBitmap); + +ClearScreen(ColorWhite()); +DrawBitmap(imageBitmap, 0, 0); +FillCircle(ColorRed(), CircleAt(centerPoint, 5)); +RefreshScreen(); + +Delay(5000); + +CloseAllWindows(); \ No newline at end of file diff --git a/public/usage-examples/graphics/bitmap_center-1-example.cpp b/public/usage-examples/graphics/bitmap_center-1-example.cpp new file mode 100644 index 000000000..95c8e3a66 --- /dev/null +++ b/public/usage-examples/graphics/bitmap_center-1-example.cpp @@ -0,0 +1,19 @@ +#include "splashkit.h" + +int main() +{ + open_window("Bitmap Center", 800, 600); + + bitmap image_bitmap = load_bitmap("image_bitmap", "image1.jpg"); + point_2d center_point = bitmap_center(image_bitmap); + + clear_screen(color_white()); + draw_bitmap(image_bitmap, 0, 0); + fill_circle(color_red(), circle_at(center_point, 5)); + refresh_screen(); + + delay(5000); + + close_all_windows(); + return 0; +} \ No newline at end of file diff --git a/public/usage-examples/graphics/bitmap_center-1-example.png b/public/usage-examples/graphics/bitmap_center-1-example.png new file mode 100644 index 000000000..c365d4ffe Binary files /dev/null and b/public/usage-examples/graphics/bitmap_center-1-example.png differ diff --git a/public/usage-examples/graphics/bitmap_center-1-example.py b/public/usage-examples/graphics/bitmap_center-1-example.py new file mode 100644 index 000000000..255a56668 --- /dev/null +++ b/public/usage-examples/graphics/bitmap_center-1-example.py @@ -0,0 +1,15 @@ +from splashkit import * + +open_window("Bitmap Center", 800, 600) + +image_bitmap = load_bitmap("image_bitmap", "image1.jpg") +center_point = bitmap_center(image_bitmap) + +clear_screen(color_white()) +draw_bitmap(image_bitmap, 0, 0) +fill_circle_record(color_red(), circle_at(center_point, 5)) +refresh_screen() + +delay(5000) + +close_all_windows() \ No newline at end of file diff --git a/public/usage-examples/graphics/bitmap_center-1-example.txt b/public/usage-examples/graphics/bitmap_center-1-example.txt new file mode 100644 index 000000000..04c2260fc --- /dev/null +++ b/public/usage-examples/graphics/bitmap_center-1-example.txt @@ -0,0 +1,5 @@ +Draw a bitmap with a red dot at its center + +:::note +To test this example code you can download these [**Resources**](/usage-examples/graphics/bitmap_center-1-example-resources.zip). +::: \ No newline at end of file diff --git a/public/usage-examples/graphics/draw_circle-1-example b/public/usage-examples/graphics/draw_circle-1-example new file mode 100644 index 000000000..e69de29bb diff --git a/public/usage-examples/graphics/draw_circle-1-example-oop.cs b/public/usage-examples/graphics/draw_circle-1-example-oop.cs new file mode 100644 index 000000000..48fd2dffe --- /dev/null +++ b/public/usage-examples/graphics/draw_circle-1-example-oop.cs @@ -0,0 +1,37 @@ +using SplashKitSDK; + +namespace DrawCircleExample +{ + public class Program + { + public static void Main() + { + SplashKit.OpenWindow("Draw Circle Example", 800, 600); + + SplashKit.ClearScreen(Color.White); + + // Draw a large red filled circle in the center + SplashKit.FillCircle(Color.Red, 400, 300, 100); + + // Draw circles with different colors, sizes, and positions + SplashKit.DrawCircle(Color.Blue, 200, 150, 80); + SplashKit.DrawCircle(Color.Green, 600, 150, 60); + SplashKit.DrawCircle(Color.Orange, 200, 450, 70); + SplashKit.DrawCircle(Color.Purple, 600, 450, 65); + + // Draw smaller circles with varying colors + for (int i = 0; i < 8; i++) + { + int radius = 20 + i * 5; + int x = 400 + (i - 4) * 80; + int y = 100; + SplashKit.DrawCircle(Color.RandomRGB(255), x, y, radius); + } + + SplashKit.RefreshScreen(); + + SplashKit.Delay(5000); + SplashKit.CloseAllWindows(); + } + } +} diff --git a/public/usage-examples/graphics/draw_circle-1-example-top-level.cs b/public/usage-examples/graphics/draw_circle-1-example-top-level.cs new file mode 100644 index 000000000..55214a807 --- /dev/null +++ b/public/usage-examples/graphics/draw_circle-1-example-top-level.cs @@ -0,0 +1,28 @@ +using static SplashKitSDK.SplashKit; + +OpenWindow("Draw Circle Example", 800, 600); + +ClearScreen(ColorWhite()); + +// Draw a large red filled circle in the center +FillCircle(ColorRed(), 400, 300, 100); + +// Draw circles with different colors, sizes, and positions +DrawCircle(ColorBlue(), 200, 150, 80); +DrawCircle(ColorGreen(), 600, 150, 60); +DrawCircle(ColorOrange(), 200, 450, 70); +DrawCircle(ColorPurple(), 600, 450, 65); + +// Draw smaller circles with varying colors +for (int i = 0; i < 8; i++) +{ + int radius = 20 + i * 5; + int x = 400 + (i - 4) * 80; + int y = 100; + DrawCircle(RandomRGBColor(255), x, y, radius); +} + +RefreshScreen(); + +Delay(5000); +CloseAllWindows(); diff --git a/public/usage-examples/graphics/draw_circle-1-example.cpp b/public/usage-examples/graphics/draw_circle-1-example.cpp new file mode 100644 index 000000000..890d9d97f --- /dev/null +++ b/public/usage-examples/graphics/draw_circle-1-example.cpp @@ -0,0 +1,33 @@ +#include "splashkit.h" + +int main() +{ + open_window("Draw Circle Example", 800, 600); + + clear_screen(COLOR_WHITE); + + // Draw a large red filled circle in the center + fill_circle(COLOR_RED, 400, 300, 100); + + // Draw circles with different colors, sizes, and positions + draw_circle(COLOR_BLUE, 200, 150, 80); + draw_circle(COLOR_GREEN, 600, 150, 60); + draw_circle(COLOR_ORANGE, 200, 450, 70); + draw_circle(COLOR_PURPLE, 600, 450, 65); + + // Draw smaller circles with varying colors + for (int i = 0; i < 8; i++) + { + int radius = 20 + i * 5; + int x = 400 + (i - 4) * 80; + int y = 100; + draw_circle(random_rgb_color(255), x, y, radius); + } + + refresh_screen(); + + delay(5000); + close_all_windows(); + + return 0; +} diff --git a/public/usage-examples/graphics/draw_circle-1-example.png b/public/usage-examples/graphics/draw_circle-1-example.png new file mode 100644 index 000000000..feeaba23b Binary files /dev/null and b/public/usage-examples/graphics/draw_circle-1-example.png differ diff --git a/public/usage-examples/graphics/draw_circle-1-example.py b/public/usage-examples/graphics/draw_circle-1-example.py new file mode 100644 index 000000000..cee257c47 --- /dev/null +++ b/public/usage-examples/graphics/draw_circle-1-example.py @@ -0,0 +1,26 @@ +from splashkit import * + +open_window("Draw Circle Example", 800, 600) + +clear_screen(color_white()) + +# Draw a large red filled circle in the center +fill_circle(color_red(), 400, 300, 100) + +# Draw circles with different colors, sizes, and positions +draw_circle(color_blue(), 200, 150, 80) +draw_circle(color_green(), 600, 150, 60) +draw_circle(color_orange(), 200, 450, 70) +draw_circle(color_purple(), 600, 450, 65) + +# Draw smaller circles with varying colors +for i in range(8): + radius = 20 + i * 5 + x = 400 + (i - 4) * 80 + y = 100 + draw_circle(random_rgb_color(255), x, y, radius) + +refresh_screen() + +delay(5000) +close_all_windows() diff --git a/public/usage-examples/graphics/draw_circle-1-example.txt b/public/usage-examples/graphics/draw_circle-1-example.txt new file mode 100644 index 000000000..01d0d42a3 --- /dev/null +++ b/public/usage-examples/graphics/draw_circle-1-example.txt @@ -0,0 +1 @@ +Circle Showcase diff --git a/public/usage-examples/graphics/get_font_style-1-example-oop.cs b/public/usage-examples/graphics/get_font_style-1-example-oop.cs new file mode 100644 index 000000000..dda4168a4 --- /dev/null +++ b/public/usage-examples/graphics/get_font_style-1-example-oop.cs @@ -0,0 +1,55 @@ +using SplashKitSDK; + +namespace GetFontStyleExample +{ + public class Program + { + public static void Main() + { + SplashKit.OpenWindow("Get Font Style", 800, 600); + + int style_number = -1; + Font font = SplashKit.FontNamed("Century.ttf"); + + while (!SplashKit.QuitRequested()) + { + SplashKit.ProcessEvents(); + + if (style_number < 3) + { + style_number += 1; + } + else + { + style_number = 0; + } + + if (style_number == 0) + { + SplashKit.SetFontStyle(font, FontStyle.NormalFont); + } + else if (style_number == 1) + { + SplashKit.SetFontStyle(font, FontStyle.BoldFont); + } + else if (style_number == 2) + { + SplashKit.SetFontStyle(font, FontStyle.ItalicFont); + } + else if (style_number == 3) + { + SplashKit.SetFontStyle(font, FontStyle.UnderlineFont); + } + + SplashKit.ClearScreen(Color.White); + // Function is used here ↓ + SplashKit.DrawText("The assigned font style is currently set to " + SplashKit.GetFontStyle(font), Color.Black, 40, 60); + SplashKit.DrawText("The quick brown fox jumps over the lazy dog", Color.Black, font, 30, 40, 110); + SplashKit.RefreshScreen(); + + SplashKit.Delay(2000); + } + SplashKit.CloseAllWindows(); + } + } +} \ No newline at end of file diff --git a/public/usage-examples/graphics/get_font_style-1-example-resources.zip b/public/usage-examples/graphics/get_font_style-1-example-resources.zip new file mode 100644 index 000000000..3fd052b33 Binary files /dev/null and b/public/usage-examples/graphics/get_font_style-1-example-resources.zip differ diff --git a/public/usage-examples/graphics/get_font_style-1-example-top-level.cs b/public/usage-examples/graphics/get_font_style-1-example-top-level.cs new file mode 100644 index 000000000..b8b2b20fb --- /dev/null +++ b/public/usage-examples/graphics/get_font_style-1-example-top-level.cs @@ -0,0 +1,47 @@ +using SplashKitSDK; +using static SplashKitSDK.SplashKit; + +OpenWindow("Get Font Style", 800, 600); + +int style_number = -1; +Font font = FontNamed("Century.ttf"); + +while (!QuitRequested()) +{ + ProcessEvents(); + + if (style_number < 3) + { + style_number += 1; + } + else + { + style_number = 0; + } + + if (style_number == 0) + { + SetFontStyle(font, FontStyle.NormalFont); + } + else if (style_number == 1) + { + SetFontStyle(font, FontStyle.BoldFont); + } + else if (style_number == 2) + { + SetFontStyle(font, FontStyle.ItalicFont); + } + else if (style_number == 3) + { + SetFontStyle(font, FontStyle.UnderlineFont); + } + + SplashKit.ClearScreen(ColorWhite()); + // Function is used here ↓ + DrawText("The assigned font style is currently set to " + GetFontStyle(font), ColorBlack(), 40, 60); + DrawText("The quick brown fox jumps over the lazy dog", ColorBlack(), font, 30, 40, 110); + RefreshScreen(); + + Delay(2000); +} +CloseAllWindows(); \ No newline at end of file diff --git a/public/usage-examples/graphics/get_font_style-1-example.cpp b/public/usage-examples/graphics/get_font_style-1-example.cpp new file mode 100644 index 000000000..5538df57c --- /dev/null +++ b/public/usage-examples/graphics/get_font_style-1-example.cpp @@ -0,0 +1,50 @@ +#include "splashkit.h" + +int main() +{ + open_window("Get Font Style", 800, 600); + + int style_number = -1; + font font = font_named("Century.ttf"); + + while (!quit_requested()) + { + process_events(); + + if (style_number < 3) + { + style_number += 1; + } + else + { + style_number = 0; + } + + if (style_number == 0) + { + set_font_style(font, NORMAL_FONT); + } + else if (style_number == 1) + { + set_font_style(font, BOLD_FONT); + } + else if (style_number == 2) + { + set_font_style(font, ITALIC_FONT); + } + else if (style_number == 3) + { + set_font_style(font, UNDERLINE_FONT); + } + + clear_screen(color_white()); + // Function is used here ↓ + draw_text("The assigned numerical font style is currently set to " + std::to_string(get_font_style(font)), color_black(), 40, 60); + draw_text("The quick brown fox jumps over the lazy dog", color_black(), font, 30, 40, 110); + refresh_screen(); + + delay(2000); + } + close_all_windows(); + return 0; +} \ No newline at end of file diff --git a/public/usage-examples/graphics/get_font_style-1-example.gif b/public/usage-examples/graphics/get_font_style-1-example.gif new file mode 100644 index 000000000..80bedce17 Binary files /dev/null and b/public/usage-examples/graphics/get_font_style-1-example.gif differ diff --git a/public/usage-examples/graphics/get_font_style-1-example.py b/public/usage-examples/graphics/get_font_style-1-example.py new file mode 100644 index 000000000..2419315a5 --- /dev/null +++ b/public/usage-examples/graphics/get_font_style-1-example.py @@ -0,0 +1,33 @@ +from splashkit import * + +open_window("Get Font Style", 800, 600) + +style_number = -1 +font = font_named("Century.ttf") + +while not quit_requested(): + process_events() + + if style_number < 3: + style_number += 1 + else: + style_number = 0 + + if style_number == 0: + set_font_style(font, FontStyle.normal_font) + elif style_number == 1: + set_font_style(font, FontStyle.bold_font) + elif style_number == 2: + set_font_style(font, FontStyle.italic_font) + elif style_number == 3: + set_font_style(font, FontStyle.underline_font) + + clear_screen_to_white() + # Function is used here ↓ + draw_text_no_font_no_size(f"The assigned font style is currently set to {get_font_style(font)}", color_black(), 40, 60) + draw_text("The quick brown fox jumps over the lazy dog", color_black(), font, 30, 40, 110) + refresh_screen() + + delay(2000) + +close_all_windows() \ No newline at end of file diff --git a/public/usage-examples/graphics/get_font_style-1-example.txt b/public/usage-examples/graphics/get_font_style-1-example.txt new file mode 100644 index 000000000..ab100a46e --- /dev/null +++ b/public/usage-examples/graphics/get_font_style-1-example.txt @@ -0,0 +1,5 @@ +The program automatically cycles through font styles for a given font (also displaying example text), with the numerical value of each style being shown on screen + +:::note +To test this example code you can download these [**Resources**](/usage-examples/graphics/get_font_style-1-example-resources.zip). +::: \ No newline at end of file diff --git a/public/usage-examples/windows/close_window-1-example-oop.cs b/public/usage-examples/windows/close_window-1-example-oop.cs new file mode 100644 index 000000000..fbc98c534 --- /dev/null +++ b/public/usage-examples/windows/close_window-1-example-oop.cs @@ -0,0 +1,62 @@ +using SplashKitSDK; + +namespace CloseWindowExample +{ + public class Program + { + public static void Main() + { + // open a window + Window wind = SplashKit.OpenWindow("DON'T CLICK THE BUTTON!", 400, 200); + + bool countdownStarted = false; + int countdown = 5; + SplashKitSDK.Timer countdownTimer = new SplashKitSDK.Timer("countdown"); + + // main loop + while (!SplashKit.QuitRequested()) + { + // get user events + SplashKit.ProcessEvents(); + + // clear screen + SplashKit.ClearWindow(wind, Color.White); + + if (!countdownStarted) + { + // Show the button before countdown starts + if (SplashKit.Button("Click Me!", SplashKit.RectangleFrom(150, 85, 100, 30))) + { + countdownStarted = true; + SplashKit.StartTimer(countdownTimer); + } + } + else + { + // Display countdown + SplashKit.DrawText($"This window will self destruct in {countdown}", Color.Black, "arial", 18, 50, 85); + + // Check if 1 second has passed + if (SplashKit.TimerTicks(countdownTimer) > 1000) + { + countdown--; + SplashKit.ResetTimer(countdownTimer); + + if (countdown <= 0) + { + SplashKit.CloseWindow(wind); + break; + } + } + } + + // draw interface and refresh + SplashKit.DrawInterface(); + SplashKit.RefreshWindow(wind); + } + + // close all open windows + SplashKit.CloseAllWindows(); + } + } +} \ No newline at end of file diff --git a/public/usage-examples/windows/close_window-1-example-top-level.cs b/public/usage-examples/windows/close_window-1-example-top-level.cs new file mode 100644 index 000000000..a21da0095 --- /dev/null +++ b/public/usage-examples/windows/close_window-1-example-top-level.cs @@ -0,0 +1,55 @@ +using SplashKitSDK; +using static SplashKitSDK.SplashKit; + +// open a window +Window wind = OpenWindow("DON'T CLICK THE BUTTON!", 400, 200); + +bool countdownStarted = false; +int countdown = 5; +// SplashKitSDK.Timer needed to distinguish from System.Threading.Timer +SplashKitSDK.Timer countdownTimer = new SplashKitSDK.Timer("countdown"); + +// main loop +while (!QuitRequested()) +{ + // get user events + ProcessEvents(); + + // clear screen + ClearWindow(wind, ColorWhite()); + + if (!countdownStarted) + { + // Show the button before countdown starts + if (Button("Click Me!", RectangleFrom(150, 85, 100, 30))) + { + countdownStarted = true; + StartTimer(countdownTimer); + } + } + else + { + // Display countdown + DrawText($"This window will self destruct in {countdown}", ColorBlack(), "arial", 18, 50, 85); + + // Check if 1 second has passed + if (TimerTicks(countdownTimer) > 1000) + { + countdown--; + ResetTimer(countdownTimer); + + if (countdown <= 0) + { + CloseWindow(wind); + break; + } + } + } + + // draw interface and refresh + DrawInterface(); + RefreshWindow(wind); +} + +// close all open windows +CloseAllWindows(); \ No newline at end of file diff --git a/public/usage-examples/windows/close_window-1-example.cpp b/public/usage-examples/windows/close_window-1-example.cpp new file mode 100644 index 000000000..529180c2e --- /dev/null +++ b/public/usage-examples/windows/close_window-1-example.cpp @@ -0,0 +1,54 @@ +#include "splashkit.h" + +int main() +{ + // open a window + window wind = open_window("DON'T CLICK THE BUTTON!", 400, 200); + + bool countdown_started = false; + int countdown = 5; + timer countdown_timer = create_timer("countdown"); + + while (!quit_requested()) + { + // get user events + process_events(); + + // clear screen + clear_window(wind, COLOR_WHITE); + + if (!countdown_started) + { + // Show the button before countdown starts + if (button("Click Me!", rectangle_from(150, 85, 100, 30))) + { + countdown_started = true; + start_timer(countdown_timer); + } + } + else + { + // Display countdown + draw_text("This window will self destruct in " + std::to_string(countdown), COLOR_BLACK, "arial", 18, 50, 85); + + // Check if 1 second has passed + if (timer_ticks(countdown_timer) > 1000) + { + countdown--; + reset_timer(countdown_timer); + + if (countdown <= 0) + { + close_window(wind); + break; + } + } + } + + draw_interface(); + refresh_window(wind); + } + + // close all open windows + close_all_windows(); +} \ No newline at end of file diff --git a/public/usage-examples/windows/close_window-1-example.gif b/public/usage-examples/windows/close_window-1-example.gif new file mode 100644 index 000000000..b746c40ba Binary files /dev/null and b/public/usage-examples/windows/close_window-1-example.gif differ diff --git a/public/usage-examples/windows/close_window-1-example.py b/public/usage-examples/windows/close_window-1-example.py new file mode 100644 index 000000000..fd1c88f18 --- /dev/null +++ b/public/usage-examples/windows/close_window-1-example.py @@ -0,0 +1,41 @@ +from splashkit import * + +# open a window +wind = open_window("DON'T CLICK THE BUTTON!", 400, 200) + +countdown_started = False +countdown = 5 +countdown_timer = create_timer("countdown") + +# main loop +while not quit_requested(): + # get user events + process_events() + + # clear screen + clear_window(wind, color_white()) + + if not countdown_started: + # Show the button before countdown starts + if button_at_position("Click Me!", rectangle_from(150, 85, 100, 30)): + countdown_started = True + start_timer(countdown_timer) + else: + # Display countdown + draw_text_font_as_string(f"This window will self destruct in {countdown}", color_black(), "arial", 18, 50, 85) + + # Check if 1 second has passed + if timer_ticks(countdown_timer) > 1000: + countdown -= 1 + reset_timer(countdown_timer) + + if countdown <= 0: + close_window(wind) + break + + # draw interface and refresh + draw_interface() + refresh_window(wind) + +# close all open windows +close_all_windows() \ No newline at end of file diff --git a/public/usage-examples/windows/close_window-1-example.txt b/public/usage-examples/windows/close_window-1-example.txt new file mode 100644 index 000000000..d11ddb123 --- /dev/null +++ b/public/usage-examples/windows/close_window-1-example.txt @@ -0,0 +1 @@ +Starts a countdown to close the window at the push of a button. \ No newline at end of file diff --git a/scripts/api-pages-script.cjs b/scripts/api-pages-script.cjs index f290d5ee5..3a625e378 100644 --- a/scripts/api-pages-script.cjs +++ b/scripts/api-pages-script.cjs @@ -101,7 +101,7 @@ function getAllFinishedExamples() { var apiData = fs.readFileSync(`${__dirname}/json-files/api.json`); apiJsonData = JSON.parse(apiData); } catch (error) { - console.error(kluer.red("Error occurred when trying to parse API Json data: ", error)); + console.error(kleur.red("Error occurred when trying to parse API Json data: ", error)); } const categories = [] diff --git a/src/content/docs/guides/Camera/1-dynamic-camera-control.mdx b/src/content/docs/guides/Camera/1-dynamic-camera-control.mdx new file mode 100644 index 000000000..dbd42a3b4 --- /dev/null +++ b/src/content/docs/guides/Camera/1-dynamic-camera-control.mdx @@ -0,0 +1,1404 @@ +--- +title: Dynamic Camera Control +description: Explore zooming into a bitmap and camera movements with mouse clicks +author: Sharvani Kandala, Vishnu Vengadeswaran +lastupdated: 02 Aug 2025 +category: Guides +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +**{frontmatter.description}** +Written by: {frontmatter.author} +_Last updated: {frontmatter.lastupdated}_ + +--- + +This tutorial shows how to use the camera in SplashKit to create interactive, dynamic game experiences. We'll focus on zooming into specific areas and making the camera follow a moving bitmap as per user clicks, allowing you to create more immersive applications. + +## Camera Movements and Zooming + +The camera in SplashKit enables navigation through a game world larger than the visible window. By adjusting the camera’s position, you can control the part of the world that the player sees. This tutorial explores using camera functions to move the camera and adjust the positions as required. + +## SplashKit Functions Used in This Tutorial + +- [MoveCameraBy](https://splashkit.io/api/camera/#move-camera-by-2) +- [SetCameraPosition](https://splashkit.io/api/camera/#set-camera-position) +- [CameraPosition](https://splashkit.io/api/camera/#camera-position) +- [OptionDefaults](https://splashkit.io/api/graphics/#option-defaults) +- [OptionScaleBmp](https://splashkit.io/api/graphics/#option-scale-bmp) +- [DrawBitmap](https://splashkit.io/api/graphics/#draw-bitmap-4) + +## Zooming into a Bitmap + +This example demonstrates how to implement a zoom effect on a preloaded bitmap of a colored grid. When the user clicks on an area of the grid, the camera zooms into the clicked section. The zoomed-in area fills the screen, and pressing the ESC key resets the view to show the entire grid again. + +:::note +To test the below code you can download these [**Resources**](/resources/guides/graphics/basics/BasicGraphicsResources.zip). + +This includes sample images. Use image "color-grid.png" for this example. +::: + + + + +```cpp +#include "splashkit.h" + +// Zoom scale factor +const int ZOOM_SCALE = 5; + +// Initial camera position at (0,0) +point_2d camera_pos = { 0, 0 }; +const int BOX_SIZE = 100; +bool zoomed_in = false; + +int main() +{ + // Open window with specified dimensions + open_window("Camera Zooming Example", 500, 550); + + // Load bitmap for the grid of colors + load_bitmap("color_grid", "color-grid.png"); + + while (!window_close_requested("Camera Zooming Example")) + { + process_events(); + + // Zoom In + if (mouse_clicked(mouse_button(LEFT_BUTTON)) && !zoomed_in) + { + int clicked_box_x = (int)(mouse_x()) / BOX_SIZE; + int clicked_box_y = (int)(mouse_y()) / BOX_SIZE; + + // Update the camera position to center on the clicked box + camera_pos.x = (clicked_box_x * BOX_SIZE - BOX_SIZE * 2) * ZOOM_SCALE; + camera_pos.y = (clicked_box_y * BOX_SIZE - BOX_SIZE * 2) * ZOOM_SCALE; + + // Update the camera position + set_camera_position(camera_pos); + + zoomed_in = true; + } + + // If Escape key is pressed, return to the full grid view + if (key_typed(key_code(ESCAPE_KEY)) && zoomed_in) + { + camera_pos.x = 0; + camera_pos.y = 0; + + // Reset the camera position + set_camera_position(camera_pos); + + zoomed_in = false; + } + + // Draw a 5x5 grid of different colored boxes + clear_screen(); + if (!zoomed_in) + { + draw_bitmap("color_grid", 0, 0, option_defaults()); + } + else + { + draw_bitmap("color_grid", 0, 0, option_scale_bmp(ZOOM_SCALE, ZOOM_SCALE)); + } + fill_rectangle(color_light_gray(), 0, 500, screen_width(), 50, option_to_screen()); + draw_text("Click a box to zoom in. Press ESC to return to grid", color_black(), 10, screen_height() - 30, option_to_screen()); + refresh_screen(); + } + + // Close the window when finished + close_all_windows(); + + return 0; +} +``` + + + + + + + + ```csharp + using SplashKitSDK; + using static SplashKitSDK.SplashKit; + + // Zoom scale factor + const int zoomScale = 5; + + // Initial camera position at (0,0) + Point2D cameraPosition = new Point2D { X = 0, Y = 0 }; + int boxSize = 100; + bool zoomedIn = false; + + // Open window with specified dimensions + OpenWindow("Camera Zooming Example", 500, 550); + + // Load bitmap for the grid of colors + LoadBitmap("color-grid", "color-grid.png"); + + while (!WindowCloseRequested("Camera Zooming Example")) + { + ProcessEvents(); + // Zoom In + if (MouseClicked(MouseButton.LeftButton) && !zoomedIn) + { + int clickedBoxX = (int)MouseX() / boxSize; + int clickedBoxY = (int)MouseY() / boxSize; + + + + // Update the camera position to center on the clicked box + cameraPosition.X = (clickedBoxX * boxSize - boxSize * 2) * zoomScale; + cameraPosition.Y = (clickedBoxY * boxSize - boxSize * 2) * zoomScale; + + SetCameraPosition(cameraPosition); + + zoomedIn = true; + } + + // If Escape key is pressed, return to the full grid view + if (KeyTyped(KeyCode.EscapeKey) && zoomedIn) + { + cameraPosition.X = 0; + cameraPosition.Y = 0; + + SetCameraPosition(cameraPosition); + + zoomedIn = false; + } + + // Draw a 5x5 grid of different colored boxes + ClearScreen(); + if (!zoomedIn) + { + DrawBitmap("color-grid", 0, 0, OptionDefaults()); + } + else + { + DrawBitmap("color-grid", 0, 0, OptionScaleBmp(zoomScale, zoomScale)); + } + FillRectangle(ColorLightGray(), 0, 500, ScreenWidth(), 50, OptionToScreen()); + DrawText("Click a box to zoom in. Press ESC to return to grid", ColorBlack(), 10, ScreenHeight() - 30, OptionToScreen()); + RefreshScreen(); + + } + + // Close the window when finished + CloseAllWindows(); +``` + + + + + +```csharp +using SplashKitSDK; + +namespace DynamicCamera +{ + public class Program + { + public static void Main() + { + // Zoom scale factor + const int zoomScale = 5; + + // Initial camera position at (0,0) + Point2D cameraPosition = new Point2D { X = 0, Y = 0 }; + int boxSize = 100; + bool zoomedIn = false; + + // Open window with specified dimensions + SplashKit.OpenWindow("Camera Zooming Example", 500, 550); + + // Load bitmap for the grid of colors + SplashKit.LoadBitmap("color-grid", "color-grid.png"); + + while (!SplashKit.WindowCloseRequested("Camera Zooming Example")) + { + SplashKit.ProcessEvents(); + // Zoom In + if (SplashKit.MouseClicked(MouseButton.LeftButton) && !zoomedIn) + { + int clickedBoxX = (int)SplashKit.MouseX() / boxSize; + int clickedBoxY = (int)SplashKit.MouseY() / boxSize; + + + + // Update the camera position to center on the clicked box + cameraPosition.X = (clickedBoxX * boxSize - boxSize * 2) * zoomScale; + cameraPosition.Y = (clickedBoxY * boxSize - boxSize * 2) * zoomScale; + + SplashKit.SetCameraPosition(cameraPosition); + + zoomedIn = true; + } + + // If Escape key is pressed, return to the full grid view + if (SplashKit.KeyTyped(KeyCode.EscapeKey) && zoomedIn) + { + cameraPosition.X = 0; + cameraPosition.Y = 0; + + SplashKit.SetCameraPosition(cameraPosition); + + zoomedIn = false; + } + + // Draw a 5x5 grid of different colored boxes + SplashKit.ClearScreen(); + if (!zoomedIn) + { + SplashKit.DrawBitmap("color-grid", 0, 0, SplashKit.OptionDefaults()); + } + else + { + SplashKit.DrawBitmap("color-grid", 0, 0, SplashKit.OptionScaleBmp(zoomScale, zoomScale)); + } + SplashKit.FillRectangle(Color.LightGray, 0, 500, SplashKit.ScreenWidth(), 50, SplashKit.OptionToScreen()); + SplashKit.DrawText("Click a box to zoom in. Press ESC to return to grid", Color.Black, 10, SplashKit.ScreenHeight() - 30, SplashKit.OptionToScreen()); + SplashKit.RefreshScreen(); + + } + + // Close the window when finished + SplashKit.CloseAllWindows(); + } + } +} +``` + + + + + + +```python +from splashkit import * + +# Zoom scale factor +ZOOM_SCALE = 5 + +# Initial camera position at (0,0) +camera_pos = point_at(0, 0) +BOX_SIZE = 100 +zoomed_in = False + +# Open window with specified dimensions +open_window("Camera Zooming Example", 500, 550) + +# Load bitmap for the grid of colors +load_bitmap("color_grid", "color-grid.png") +color_grid = bitmap_named("color_grid") + +while not window_close_requested_named("Camera Zooming Example"): + process_events() + + # Zoom In + if mouse_clicked(MouseButton.left_button) and not zoomed_in: + clicked_box_x = int(mouse_x()) // BOX_SIZE + clicked_box_y = int(mouse_y()) // BOX_SIZE + + # Update the camera position to center on the clicked box + camera_pos.x = (clicked_box_x * BOX_SIZE - BOX_SIZE * 2) * ZOOM_SCALE + camera_pos.y = (clicked_box_y * BOX_SIZE - BOX_SIZE * 2) * ZOOM_SCALE + + # Update the camera position + set_camera_position(camera_pos) + + zoomed_in = True + + # If Escape key is pressed, return to the full grid view + if key_typed(KeyCode.escape_key) and zoomed_in: + camera_pos.x = 0 + camera_pos.y = 0 + + # Reset the camera position + set_camera_position(camera_pos) + + zoomed_in = False + + # Draw a 5x5 grid of different colored boxes + clear_screen(color_white()) + if not zoomed_in: + draw_bitmap_with_options(color_grid, 0, 0, option_defaults()) + else: + draw_bitmap_with_options(color_grid, 0, 0, option_scale_bmp( + ZOOM_SCALE, ZOOM_SCALE)) + + fill_rectangle_with_options(color_light_gray(), 0, 500, + screen_width(), 50, option_to_screen()) + draw_text_no_font_no_size_with_options("Click a box to zoom in. Press ESC to return to grid", + color_black(), 10, screen_height() - 30, option_to_screen()) + refresh_screen() + +# Close the window when finished +close_all_windows() +``` + + + + + +### Understanding the code for zooming + +Let’s break this code into smaller sections and analyze each part. + +#### Setting Up the Window and Loading the Bitmap + +In this section, we’ll start by creating a window and loading a bitmap that represents a grid of colors. We also define the camera's zoom factor and initial position. + + + + +```cpp +#include "splashkit.h" + +// Zoom scale factor +const int ZOOM_SCALE = 5; + +// Initial camera position at (0,0) +point_2d camera_pos = { 0, 0 }; +const int BOX_SIZE = 100; +bool zoomed_in = false; + +int main() +{ + // Open window with specified dimensions + open_window("Camera Zooming Example", 500, 550); + + // Load bitmap for the grid of colors + load_bitmap("color_grid", "color-grid.png"); +} +``` + + + + + + + +```csharp +using SplashKitSDK; +using static SplashKitSDK.SplashKit; + +// Zoom scale factor +const int zoomScale = 5; + +// Initial camera position at (0,0) +Point2D cameraPosition = new Point2D { X = 0, Y = 0 }; +int boxSize = 100; +bool zoomedIn = false; + +// Open window with specified dimensions +OpenWindow("Camera Zooming Example", 500, 550); + +// Load bitmap for the grid of colors +LoadBitmap("color-grid", "color-grid.png"); +``` + + + + + +```csharp +using SplashKitSDK; + +// Zoom scale factor +const int zoomScale = 5; + +// Initial camera position at (0,0) +Point2D cameraPosition = new Point2D { X = 0, Y = 0 }; +int boxSize = 100; +bool zoomedIn = false; + +// Open window with specified dimensions +SplashKit.OpenWindow("Camera Zooming Example", 500, 550); + +// Load bitmap for the grid of colors +SplashKit.LoadBitmap("color-grid", "color-grid.png"); + +``` + + + + + +```python +from splashkit import * + +# Zoom scale factor +ZOOM_SCALE = 5 + +# Initial camera position at (0,0) +camera_pos = point_at(0, 0) +BOX_SIZE = 100 +zoomed_in = False + +# Open window with specified dimensions +open_window("Camera Zooming Example", 500, 550) + +# Load bitmap for the grid of colors +load_bitmap("color_grid", "color-grid.png") +color_grid = bitmap_named("color_grid") +``` + + + +Here, we open a 500x550 window using [OpenWindow](https://splashkit.io/api/windows/#open-window) and load a bitmap using [LoadBitmap](https://splashkit.io/api/graphics/#load-bitmap). The [CameraPosition](https://splashkit.io/api/camera/#camera-position) is initially set to (0, 0). + +#### Implementing Zoom on Mouse Click + +Now we’ll add functionality to zoom into the clicked area. When a user clicks within the window, the camera zooms into the grid, centered around the clicked box. + + + + +```cpp +while (!window_close_requested("Camera Zooming Example")) +{ + process_events(); + + // Zoom In + if (mouse_clicked(LEFT_BUTTON) && !zoomed_in) + { + int clicked_box_x = (int)(mouse_x()) / BOX_SIZE; + int clicked_box_y = (int)(mouse_y()) / BOX_SIZE; + + // Update the camera position to center on the clicked box + camera_pos.x = (clicked_box_x * BOX_SIZE - BOX_SIZE * 2) * ZOOM_SCALE; + camera_pos.y = (clicked_box_y * BOX_SIZE - BOX_SIZE * 2) * ZOOM_SCALE; + + set_camera_position(camera_pos); + zoomed_in = true; + } + } + +``` + + + + + + +```csharp +while (!WindowCloseRequested("Camera Zooming Example")) +{ + ProcessEvents(); + + // Zoom In + if (MouseClicked(MouseButton.LeftButton) && !zoomedIn) + { + int clickedBoxX = (int)MouseX() / boxSize; + int clickedBoxY = (int)MouseY() / boxSize; + + // Update the camera position to center on the clicked box + cameraPosition.X = (clickedBoxX * boxSize - boxSize * 2) * zoomScale; + cameraPosition.Y = (clickedBoxY * boxSize - boxSize * 2) * zoomScale; + + SetCameraPosition(cameraPosition); + zoomedIn = true; + } +} + +``` + + + +```csharp +while (!SplashKit.WindowCloseRequested("Camera Zooming Example")) +{ + SplashKit.ProcessEvents(); + + // Zoom In + if (SplashKit.MouseClicked(MouseButton.LeftButton) && !zoomedIn) + { + int clickedBoxX = (int)SplashKit.MouseX() / boxSize; + int clickedBoxY = (int)SplashKit.MouseY() / boxSize; + + // Update the camera position to center on the clicked box + cameraPosition.X = (clickedBoxX * boxSize - boxSize * 2) * zoomScale; + cameraPosition.Y = (clickedBoxY * boxSize - boxSize * 2) * zoomScale; + + SplashKit.SetCameraPosition(cameraPosition); + zoomedIn = true; + } +} +``` + + + + + +```python +while not window_close_requested_named("Camera Zooming Example"): + process_events() + + # Zoom In + if mouse_clicked(MouseButton.left_button) and not zoomed_in: + clicked_box_x = int(mouse_x()) // BOX_SIZE + clicked_box_y = int(mouse_y()) // BOX_SIZE + + # Update the camera position to center on the clicked box + camera_pos.x = (clicked_box_x * BOX_SIZE - BOX_SIZE * 2) * ZOOM_SCALE + camera_pos.y = (clicked_box_y * BOX_SIZE - BOX_SIZE * 2) * ZOOM_SCALE + + # Update the camera position + set_camera_position(camera_pos) + + zoomed_in = True + +``` + + + +Here, we use [Mouse Clicked](https://splashkit.io/api/input/#mouse-clicked) to detect when the user clicks inside the window. If the left mouse button is clicked and the camera is not already zoomed in, we calculate the coordinates of the clicked box by dividing the mouse's position by "BOX SIZE". +Once we know which box was clicked, we update the camera position. The function [SetCameraPosition](https://splashkit.io/api/camera/#set-camera-position) is used to reposition the camera so that the clicked box is centered in the view. The camera’s position is updated based on the new camera position values, which take into account both the clicked box and the zoom factor. This zooms into the selected area by moving the camera and enlarging the view, creating the zoom-in effect. + +#### Resetting the Camera on Key Press + +When the Escape key is pressed, the camera will reset to its original position, showing the entire grid again. + + + + +```cpp +// If Escape key is pressed, return to the full grid view +if (key_typed(ESCAPE_KEY) && zoomed_in) +{ + camera_pos.x = 0; + camera_pos.y = 0; + set_camera_position(camera_pos); + zoomed_in = false; +} + +``` + + + + + + +```csharp +// If Escape key is pressed, return to the full grid view +if (KeyTyped(KeyCode.EscapeKey) && zoomedIn) +{ + cameraPosition.X = 0; + cameraPosition.Y = 0; + SetCameraPosition(cameraPosition); + zoomedIn = false; +} + +``` + + + +```csharp +// If Escape key is pressed, return to the full grid view +if (SplashKit.KeyTyped(KeyCode.EscapeKey) && zoomedIn) +{ + cameraPosition.X = 0; + cameraPosition.Y = 0; + SplashKit.SetCameraPosition(cameraPosition); + zoomedIn = false; +} +``` + + + + + +```python +# If Escape key is pressed, return to the full grid view +if key_typed(KeyCode.escape_key) and zoomed_in: + camera_pos.x = 0 + camera_pos.y = 0 + + # Reset the camera position + set_camera_position(camera_pos) + zoomed_in = False +``` + + + +Here, the [Key Typed](https://splashkit.io/api/input/#key-typed) function is used to detect whether the "ESCAPE KEY" was pressed. When the "ESCAPE KEY" is pressed and the view is zoomed in, the camera's position is reset by setting camera position values to 0. This moves the camera back to the original full grid view. The function [SetCameraPosition](https://splashkit.io/api/camera/#set-camera-position) is then called to update the camera position. + +#### Drawing the Bitmap and Refreshing the Screen + +In this final part, we draw the bitmap and refresh the screen. If zooming is active, the bitmap is scaled according to the ZOOM_SCALE. + + + +```cpp +// Draw the grid with or without zoom +clear_screen(); +if (!zoomed_in) +{ + draw_bitmap("color_grid", 0, 0, option_defaults()); +} +else +{ + draw_bitmap("color_grid", 0, 0, option_scale_bmp(ZOOM_SCALE, ZOOM_SCALE)); +} +fill_rectangle(color_light_gray(), 0, 500, screen_width(), 50, option_to_screen()); +draw_text("Click a box to zoom in. Press ESC to return to grid", color_black(), 10, screen_height() - 30, option_to_screen()); +refresh_screen(); +``` + + + + + + + +```csharp + +// Draw the grid with or without zoom +ClearScreen(); +if (!zoomedIn) +{ + DrawBitmap("color-grid", 0, 0, OptionDefaults()); +} +else +{ + DrawBitmap("color-grid", 0, 0, OptionScaleBmp(zoomScale, zoomScale)); +} +FillRectangle(ColorLightGray(), 0, 500, ScreenWidth(), 50, OptionToScreen()); +DrawText("Click a box to zoom in. Press ESC to return to grid", ColorBlack(), 10, ScreenHeight() - 30, OptionToScreen()); +RefreshScreen(); + +``` + + + +```csharp +// Draw a 5x5 grid of different colored boxes +SplashKit.ClearScreen(); +if (!zoomedIn) +{ + SplashKit.DrawBitmap("color-grid", 0, 0, SplashKit.OptionDefaults()); +} +else +{ + SplashKit.DrawBitmap("color-grid", 0, 0, SplashKit.OptionScaleBmp(zoomScale, zoomScale)); +} +SplashKit.FillRectangle(Color.LightGray, 0, 500, SplashKit.ScreenWidth(), 50, SplashKit.OptionToScreen()); +SplashKit.DrawText("Click a box to zoom in. Press ESC to return to grid", Color.Black, 10, SplashKit.ScreenHeight() - 30, SplashKit.OptionToScreen()); +SplashKit.RefreshScreen(); +``` + + + + + +```python +# Draw a 5x5 grid of different colored boxes +clear_screen(color_white()) +if not zoomed_in: + draw_bitmap_with_options(color_grid, 0, 0, option_defaults()) + else: + draw_bitmap_with_options(color_grid, 0, 0, option_scale_bmp( + ZOOM_SCALE, ZOOM_SCALE)) + + fill_rectangle_with_options(color_light_gray(), 0, 500, + screen_width(), 50, option_to_screen()) + draw_text_no_font_no_size_with_options("Click a box to zoom in. Press ESC to return to grid", + color_black(), 10, screen_height() - 30, option_to_screen()) + refresh_screen() +``` + + + +In this code, the [Draw Bitmap](https://splashkit.io/api/graphics/#draw-bitmap) function displays the grid with optional zooming, using [Option Defaults](https://splashkit.io/api/graphics/#option-defaults) for default settings. The [Option Scale Bmp](https://splashkit.io/api/graphics/#option-scale-bmp) function scales the bitmap based on the zoom factor. The [Option To Screen](https://splashkit.io/api/graphics/#option-to-screen) function is used to ensure that additional elements like rectangles and text are drawn with the correct positioning and transformations based on the current screen settings. After drawing the bitmap, we refresh the screen using [Refresh Screen](https://splashkit.io/api/graphics/#refresh-screen). + +## Camera Movement and Scrolling + +This example illustrates a dynamic camera system that follows the target position within a large bitmap grid. The camera initially centers on the bitmap's middle. When the user clicks within the window, the camera’s target position updates to the mouse click location, causing the camera to move smoothly towards this new position. Additionally, scrolling with the mouse wheel allows for manual camera adjustments. The bitmap is drawn in the window, and the camera movement is continuously updated based on user input. + +:::note +To test the below code you can download these [**Resources**](/resources/guides/graphics/basics/BasicGraphicsResources.zip). + +This includes sample images. Use image "color-grid-5000.png" for this example. +::: + + + + +```cpp +#include "splashkit.h" + +// Speed of camera movement +const int SPEED = 4; + +int main() +{ + // Open a window + open_window("Camera Movement Example", 800, 600); + + // Load a 5x5 colored grid bitmap + bitmap grid = load_bitmap("grid", "color-grid-5000.png"); + + // Set the initial camera position to center the bitmap + set_camera_position( + point_at(bitmap_width(grid) / 2 - screen_center().x, + bitmap_height(grid) / 2 - screen_center().y) + ); + + // Target position where the camera will move towards + point_2d target = point_at(bitmap_width(grid) / 2, bitmap_height(grid) / 2); + + // Main game loop + while (!window_close_requested("Camera Movement Example")) + { + process_events(); + + // Set target Camera position to be centered on Mouse Position + if (mouse_clicked(LEFT_BUTTON)) + { + target = to_world(mouse_position()); + } + + // Move camera to center target position on screen + if (camera_x() < target.x - screen_width() / 2) + { + move_camera_by(SPEED, 0); + } + if (camera_x() + screen_width() > target.x + screen_width() / 2) + { + move_camera_by(-SPEED, 0); + } + if (camera_y() < target.y - screen_height() / 2) + { + move_camera_by(0, SPEED); + } + if (camera_y() + screen_height() > target.y + screen_height() / 2) + { + move_camera_by(0, -SPEED); + } + + // Allow scroll movement + if (mouse_wheel_scroll().x < 0 || mouse_wheel_scroll().x > 0 || mouse_wheel_scroll().y < 0 || mouse_wheel_scroll().y > 0 ) + { + move_camera_by(SPEED * mouse_wheel_scroll().x, 0); + move_camera_by(0, SPEED * -mouse_wheel_scroll().y); + target = screen_center(); + } + + // Draw Bitmap + clear_screen(color_black()); + draw_bitmap(grid, 0, 0); + refresh_screen(60); + } + + close_all_windows(); + return 0; +} +``` + + + + + + +```csharp +using SplashKitSDK; +using static SplashKitSDK.SplashKit; + +// Speed of camera movement +const int SPEED = 4; + +// Open a window +OpenWindow("Camera Movement Example", 800, 600); + +// Load a 5x5 colored grid bitmap +Bitmap grid = LoadBitmap("grid", "color-grid-5000.png"); + +// Set the initial camera position to center the bitmap +SetCameraPosition(PointAt(BitmapWidth(grid) / 2 - ScreenCenter().X, BitmapHeight(grid) / 2 - ScreenCenter().Y)); + +// Target position where the camera will move towards +Point2D target = PointAt(BitmapWidth(grid) / 2, BitmapHeight(grid) / 2); + +// Main game loop +while (!WindowCloseRequested("Camera Movement Example")) +{ + ProcessEvents(); + + // Set target Camera position to be centered on Mouse Position + if (MouseClicked(MouseButton.LeftButton)) + { + target = ToWorld(MousePosition()); + } + + // Move camera to center target position on screen + if (CameraX() < target.X - ScreenWidth() / 2) + { + MoveCameraBy(SPEED, 0); + } + if (CameraX() + ScreenWidth() > target.X + ScreenWidth() / 2) + { + MoveCameraBy(-SPEED, 0); + } + if (CameraY() < target.Y - ScreenHeight() / 2) + { + MoveCameraBy(0, SPEED); + } + if (CameraY() + ScreenHeight() > target.Y + ScreenHeight() / 2) + { + MoveCameraBy(0, -SPEED); + } + + // Allow scroll movement + if (MouseWheelScroll().X < 0 || MouseWheelScroll().X > 0 || MouseWheelScroll().Y < 0 || MouseWheelScroll().Y > 0) + { + MoveCameraBy(SPEED * MouseWheelScroll().X, 0); + MoveCameraBy(0, SPEED * -MouseWheelScroll().Y); + target = ScreenCenter(); + } + + // Draw Bitmap + ClearScreen(ColorBlack()); + DrawBitmap(grid, 0, 0); + RefreshScreen(60); +} + +CloseAllWindows(); +``` + + + + +```csharp +using SplashKitSDK; + +namespace DynamicCamera +{ + public class Program + { + public static void Main() + { + // Speed of camera movement + const int SPEED = 4; + + // Open a window + SplashKit.OpenWindow("Camera Movement Example", 800, 600); + + // Load a 5x5 colored grid bitmap + Bitmap grid = SplashKit.LoadBitmap("grid", "color-grid-5000.png"); + + // Set the initial camera position to center the bitmap + SplashKit.SetCameraPosition(SplashKit.PointAt(SplashKit.BitmapWidth(grid) / 2 - SplashKit.ScreenCenter().X, SplashKit.BitmapHeight(grid) / 2 - SplashKit.ScreenCenter().Y)); + + // Target position where the camera will move towards + Point2D target = SplashKit.PointAt(SplashKit.BitmapWidth(grid) / 2, SplashKit.BitmapHeight(grid) / 2); + + // Main game loop + while (!SplashKit.WindowCloseRequested("Camera Movement Example")) + { + SplashKit.ProcessEvents(); + + // Set target Camera position to be centered on Mouse Position + if (SplashKit.MouseClicked(MouseButton.LeftButton)) + { + target = SplashKit.ToWorld(SplashKit.MousePosition()); + } + + // Move camera to center target position on screen + if (SplashKit.CameraX() < target.X - SplashKit.ScreenWidth() / 2) + { + SplashKit.MoveCameraBy(SPEED, 0); + } + if (SplashKit.CameraX() + SplashKit.ScreenWidth() > target.X + SplashKit.ScreenWidth() / 2) + { + SplashKit.MoveCameraBy(-SPEED, 0); + } + if (SplashKit.CameraY() < target.Y - SplashKit.ScreenHeight() / 2) + { + SplashKit.MoveCameraBy(0, SPEED); + } + if (SplashKit.CameraY() + SplashKit.ScreenHeight() > target.Y + SplashKit.ScreenHeight() / 2) + { + SplashKit.MoveCameraBy(0, -SPEED); + } + + // Allow scroll movement + if (SplashKit.MouseWheelScroll().X < 0 || SplashKit.MouseWheelScroll().X > 0 || SplashKit.MouseWheelScroll().Y < 0 || SplashKit.MouseWheelScroll().Y > 0) + { + SplashKit.MoveCameraBy(SPEED * SplashKit.MouseWheelScroll().X, 0); + SplashKit.MoveCameraBy(0, SPEED * -SplashKit.MouseWheelScroll().Y); + target = SplashKit.ScreenCenter(); + } + + // Draw Bitmap + SplashKit.ClearScreen(Color.Black); + SplashKit.DrawBitmap(grid, 0, 0); + SplashKit.RefreshScreen(60); + } + + SplashKit.CloseAllWindows(); + } + } +} +``` + + + + + +```python +from splashkit import * + +# Speed of camera movement +SPEED = 4 + +# Open a window +open_window("Camera Movement Example", 800, 600) + +# Load a 5x5 colored grid bitmap +grid = load_bitmap("grid", "color-grid-5000.png") + + +# Set the initial camera position to center the bitmap +set_camera_position( + point_at(bitmap_width(grid) / 2 - screen_center().x, + bitmap_height(grid) / 2 - screen_center().y) +) + +# Target position where the camera will move towards +target = point_at(bitmap_width(grid) / 2, bitmap_height(grid) / 2) + +# Main game loop +while not window_close_requested_named("Camera Movement Example"): + process_events() + + # Set target Camera position to be centered on Mouse Position + if mouse_clicked(MouseButton.left_button): + target = to_world(mouse_position()) + + # Move camera to center target position on screen + if camera_x() < target.x - screen_width() / 2: + move_camera_by(SPEED, 0) + if camera_x() + screen_width() > target.x + screen_width() / 2: + move_camera_by(-SPEED, 0) + if camera_y() < target.y - screen_height() / 2: + move_camera_by(0, SPEED) + if camera_y() + screen_height() > target.y + screen_height() / 2: + move_camera_by(0, -SPEED) + + # Allow scroll movement + if (mouse_wheel_scroll().x != 0 or mouse_wheel_scroll().y != 0): + move_camera_by(SPEED * mouse_wheel_scroll().x, 0) + move_camera_by(0, SPEED * -mouse_wheel_scroll().y) + target = screen_center() + + # Draw Bitmap + clear_screen(color_black()) + draw_bitmap(grid, 0, 0) + refresh_screen_with_target_fps(60) + +close_all_windows() +``` + + + +### Understanding the code for movement + +Let’s break this code into smaller sections and analyze each part. + +#### Opening the Window and Loading the Bitmap + +We start by defining the speed of the camera movement, opening the window, and loading the bitmap that represents the game world. We then position the camera at the center of the bitmap. + + + + +```cpp +#include "splashkit.h" + +// Speed of camera movement +const int SPEED = 4; + +int main() +{ + // Open a window + open_window("Camera Movement Example", 800, 600); + + // Load a 5x5 colored grid bitmap + bitmap grid = load_bitmap("grid", "color-grid-5000.png"); + + // Set the initial camera position to center the bitmap + set_camera_position( + point_at(bitmap_width(grid) / 2 - screen_center().x, + bitmap_height(grid) / 2 - screen_center().y) + ); +} + +``` + + + + + + +```csharp +using SplashKitSDK; +using static SplashKitSDK.SplashKit; + +// Speed of camera movement +const int SPEED = 4; + +// Open a window +OpenWindow("Camera Movement Example", 800, 600); + +// Load a 5x5 colored grid bitmap +Bitmap grid = LoadBitmap("grid", "color-grid-5000.png"); + +// Set the initial camera position to center the bitmap +SetCameraPosition(PointAt(BitmapWidth(grid) / 2 - ScreenCenter().X, BitmapHeight(grid) / 2 - ScreenCenter().Y)); + + +``` + + + + +```csharp +using SplashKitSDK; + +// Speed of camera movement +const int SPEED = 4; + +// Open a window +SplashKit.OpenWindow("Camera Movement Example", 800, 600); + +// Load a 5x5 colored grid bitmap +Bitmap grid = SplashKit.LoadBitmap("grid", "color-grid-5000.png"); + +// Set the initial camera position to center the bitmap +SplashKit.SetCameraPosition(SplashKit.PointAt(SplashKit.BitmapWidth(grid) / 2 - SplashKit.ScreenCenter().X, SplashKit.BitmapHeight(grid) / 2 - SplashKit.ScreenCenter().Y)); +``` + + + + + +```python +from splashkit import * + +# Speed of camera movement +SPEED = 4 + +# Open a window +open_window("Camera Movement Example", 800, 600) + +# Load a 5x5 colored grid bitmap +grid = load_bitmap("grid", "color-grid-5000.png") + + +# Set the initial camera position to center the bitmap +set_camera_position( + point_at(bitmap_width(grid) / 2 - screen_center().x, + bitmap_height(grid) / 2 - screen_center().y) +) + +``` + + + +In this part, the [OpenWindow](https://splashkit.io/api/windows/#open-window) function creates an 800x600 window, and the [LoadBitmap](https://splashkit.io/api/graphics/#load-bitmap) function loads a large 5x5 grid bitmap. The [SetCameraPosition](https://splashkit.io/api/camera/#set-camera-position) function centers the camera on the middle of the bitmap by adjusting the camera's position based on the bitmap's width and height. + +#### Defining the Target Position for Camera Movement + +Next, we define a target position (target) where the camera will move when the user clicks inside the window. + + + + +```cpp +// Target position where the camera will move towards +point_2d target = point_at(bitmap_width(grid) / 2, bitmap_height(grid) / 2); +``` + + + + + + +```csharp + +// Target position where the camera will move towards +Point2D target = PointAt(BitmapWidth(grid) / 2, BitmapHeight(grid) / 2); + +``` + + + + +```csharp +// Target position where the camera will move towards +Point2D target = SplashKit.PointAt(SplashKit.BitmapWidth(grid) / 2, SplashKit.BitmapHeight(grid) / 2); +``` + + + + + +```python +# Target position where the camera will move towards +target = point_at(bitmap_width(grid) / 2, bitmap_height(grid) / 2) + +``` + + + +Here, we set the initial target to the center of the bitmap, which the camera will follow when the user interacts with the game world. + +#### Updating the Camera Target on Mouse Click + +In the main game loop, we listen for user inputs. If the left mouse button is clicked, the target position is updated to the mouse click’s world position. + + + + +```cpp +// Main game loop +while (!window_close_requested("Camera Movement Example")) +{ + process_events(); + + // Set target Camera position to be centered on Mouse Position + if (mouse_clicked(LEFT_BUTTON)) + { + target = to_world(mouse_position()); + } +} +``` + + + + + + +```csharp +// Main game loop +while (!WindowCloseRequested("Camera Movement Example")) +{ + ProcessEvents(); + + // Set target Camera position to be centered on Mouse Position + if (MouseClicked(MouseButton.LeftButton)) + { + target = ToWorld(MousePosition()); + } +} +``` + + + + +```csharp +// Main game loop +while (!SplashKit.WindowCloseRequested("Camera Movement Example")) +{ + SplashKit.ProcessEvents(); + + // Set target Camera position to be centered on Mouse Position + if (SplashKit.MouseClicked(MouseButton.LeftButton)) + { + target = SplashKit.ToWorld(SplashKit.MousePosition()); + } +} +``` + + + + + +```python +# Main game loop +while not window_close_requested_named("Camera Movement Example"): + process_events() + + # Set target Camera position to be centered on Mouse Position + if mouse_clicked(MouseButton.left_button): + target = to_world(mouse_position()) + +``` + + + +In this part of code, the [MouseClicked](https://splashkit.io/api/input/#mouse-clicked) function detects when the left mouse button is clicked. When clicked, the mouse's screen position is changed to a position in the game world. This updates the target to the mouse's clicked location. + +#### Smooth Camera Movement Toward the Target + +Next, the camera moves smoothly toward the target by adjusting its position horizontally and vertically until the target is centered on the screen. + + + + +```cpp +// Move camera to center target position on screen +if (camera_x() < target.x - screen_width() / 2) +{ + move_camera_by(SPEED, 0); +} +if (camera_x() + screen_width() > target.x + screen_width() / 2) +{ + move_camera_by(-SPEED, 0); +} +if (camera_y() < target.y - screen_height() / 2) +{ + move_camera_by(0, SPEED); +} +if (camera_y() + screen_height() > target.y + screen_height() / 2) +{ + move_camera_by(0, -SPEED); +} +``` + + + + + + +```csharp + +// Move camera to center target position on screen +if (CameraX() < target.X - ScreenWidth() / 2) +{ + MoveCameraBy(SPEED, 0); +} +if (CameraX() + ScreenWidth() > target.X + ScreenWidth() / 2) +{ + MoveCameraBy(-SPEED, 0); +} +if (CameraY() < target.Y - ScreenHeight() / 2) +{ + MoveCameraBy(0, SPEED); +} +if (CameraY() + ScreenHeight() > target.Y + ScreenHeight() / 2) +{ + MoveCameraBy(0, -SPEED); +} + + +``` + + + + +```csharp +// Move camera to center target position on screen +if (SplashKit.CameraX() < target.X - SplashKit.ScreenWidth() / 2) +{ + SplashKit.MoveCameraBy(SPEED, 0); +} +if (SplashKit.CameraX() + SplashKit.ScreenWidth() > target.X + SplashKit.ScreenWidth() / 2) +{ + SplashKit.MoveCameraBy(-SPEED, 0); +} +if (SplashKit.CameraY() < target.Y - SplashKit.ScreenHeight() / 2) +{ + SplashKit.MoveCameraBy(0, SPEED); +} +if (SplashKit.CameraY() + SplashKit.ScreenHeight() > target.Y + SplashKit.ScreenHeight() / 2) +{ + SplashKit.MoveCameraBy(0, -SPEED); +} +``` + + + + + +```python +# Move camera to center target position on screen +if camera_x() < target.x - screen_width() / 2: + move_camera_by(SPEED, 0) +if camera_x() + screen_width() > target.x + screen_width() / 2: + move_camera_by(-SPEED, 0) +if camera_y() < target.y - screen_height() / 2: + move_camera_by(0, SPEED) +if camera_y() + screen_height() > target.y + screen_height() / 2: + move_camera_by(0, -SPEED) + +``` + + + +This section checks if the camera's current position ([CameraX](https://splashkit.io/api/camera/#camera-x) and [CameraY](https://splashkit.io/api/camera/#camera-y)) is not centered on the target. The [Screen Width](https://splashkit.io/api/graphics/#screen-width) and [Screen Height](https://splashkit.io/api/graphics/#screen-height) functions are used to calculate half the screen's dimensions to ensure the target is centered. If the camera is off-center relative to the target, [MoveCameraBy](https://splashkit.io/api/camera/#move-camera-by-2) function moves the camera incrementally at the defined SPEED to adjust the position. This ensures that the target remains within the center of the visible screen. + +#### Handling Camera Movement with Mouse Scroll + +We also allow the camera to move manually by using the mouse wheel. Scrolling the mouse wheel in any direction will move the camera in small steps. + + + + +```cpp +// Allow scroll movement +if (mouse_wheel_scroll().x < 0 || mouse_wheel_scroll().x > 0 || mouse_wheel_scroll().y < 0 || mouse_wheel_scroll().y > 0) +{ + move_camera_by(SPEED * mouse_wheel_scroll().x, 0); + move_camera_by(0, SPEED * -mouse_wheel_scroll().y); + target = screen_center(); +} +``` + + + + + + +```csharp + +// Allow scroll movement +if (MouseWheelScroll().X < 0 || MouseWheelScroll().X > 0 || MouseWheelScroll().Y < 0 || MouseWheelScroll().Y > 0) +{ + MoveCameraBy(SPEED * MouseWheelScroll().X, 0); + MoveCameraBy(0, SPEED * -MouseWheelScroll().Y); + target = ScreenCenter(); +} + +``` + + + + +```csharp +// Allow scroll movement +if (SplashKit.MouseWheelScroll().X < 0 || SplashKit.MouseWheelScroll().X > 0 || SplashKit.MouseWheelScroll().Y < 0 || SplashKit.MouseWheelScroll().Y > 0) +{ + SplashKit.MoveCameraBy(SPEED * SplashKit.MouseWheelScroll().X, 0); + SplashKit.MoveCameraBy(0, SPEED * -SplashKit.MouseWheelScroll().Y); + target = SplashKit.ScreenCenter(); +} +``` + + + + + +```python +# Allow scroll movement +if (mouse_wheel_scroll().x != 0 or mouse_wheel_scroll().y != 0): + move_camera_by(SPEED * mouse_wheel_scroll().x, 0) + move_camera_by(0, SPEED * -mouse_wheel_scroll().y) + target = screen_center() + +``` + + + +This section detects scrolling through the [Mouse Wheel Scroll](https://splashkit.io/api/input/#mouse-wheel-scroll) function. If the user scrolls in any direction, the camera moves vertically based on the scroll input. The target is reset to the center of the screen after scrolling. + +#### Draw the Bitmap and Refreshing the Screen + +We have already discussed how to draw the bitmap and refresh the screen in the "Zooming into a Colored Grid" example above. The process remains the same here, where we clear the screen, draw the bitmap, and refresh the display to reflect any changes. + +## Conclusion + +This tutorial covered the basics of dynamic camera control in SplashKit using SetCameraPosition and MoveCameraBy functions. These examples showed how to zoom into specific areas and how to make the camera movements with mouse clicks and mouse wheel scrolls, both of which are key techniques for creating more interactive and immersive games. You can now apply these concepts to your own projects, enhancing the way your games interact with the camera and the player. diff --git a/src/content/docs/guides/Graphics/2-shape-modelling.mdx b/src/content/docs/guides/Graphics/2-shape-modelling.mdx new file mode 100644 index 000000000..1cf681018 --- /dev/null +++ b/src/content/docs/guides/Graphics/2-shape-modelling.mdx @@ -0,0 +1,1827 @@ +--- +title: Shape Modelling +description: This guide introduces the functions for managing shapes in graphical applications built with SplashKit. We will discuss how to use the API in SplashKit to create graphical application tools. +category: Guides +author: Yuyang Yang and Vishnu +lastupdated: Aug 20 2025 +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +**{frontmatter.description}** +Written by: {frontmatter.author} +_Last updated: {frontmatter.lastupdated}_ + +--- + +In this guide, you’ll learn how to create and manage graphics in SplashKit, including shape creation, movement, resizing, and connecting shapes with lines. + + +## Graphics creation and Management +In this section, we'll detail how to implement the shape creation and management functionality using design patterns and code examples. + + +### Model Design +We start by defining a ShapeModel structure to represent various basic shapes. This model holds attributes like position, size, color, and shape type. + + + +```cpp +struct ShapeModel +{ + double x, y; // Shape's starting position + double width, height; // Shape's width and height + string color; // Shape's color + double rotation; // Shape's rotation angle + string type; // Shape type (Rectangle, Circle, Triangle, etc.) +}; +``` + + + + + + +```csharp +public struct ShapeModel +{ + public double x, y; // Shape's starting position + public double width, height; // Shape's width and height + public string color; // Shape's color + public double rotation; // Shape's rotation angle + public string type; // Shape type (Rectangle, Circle, Triangle, etc.) +} +``` + + + +```csharp +public struct ShapeModel +{ + public double x, y; // Shape's starting position + public double width, height; // Shape's width and height + public string color; // Shape's color + public double rotation; // Shape's rotation angle + public string type; // Shape type (Rectangle, Circle, Triangle, etc.) +} +``` + + + + + + +```python +class ShapeModel: + def __init__(self): + self.x = 0.0 # Shape's starting position + self.y = 0.0 + self.width = 0.0 # Shape's width and height + self.height = 0.0 + self.color = "" # Shape's color + self.rotation = 0.0 # Shape's rotation angle + self.type = "" # Shape type (Rectangle, Circle, Triangle, etc.) + +``` + + +The `ShapeModel` can represent various shapes like rectangles, triangles, etc. The `type` field is used to distinguish between different shapes, allowing the correct drawing function to be called during rendering. + +### Shape Creation +The code simulates the user selecting a graphic and placing it on the canvas. When the user clicks on the canvas, the code creates a new graphic at the mouse position based on the preset shape type + + + +```cpp +if (mouse_clicked(LEFT_BUTTON)) +{ + ShapeModel new_shape; + new_shape.x = mouse_x(); // Get the X coordinate of the mouse + new_shape.y = mouse_y(); // Get the Y coordinate of the mouse + new_shape.width = 100; // Set default width + new_shape.height = 100; // Set default height + new_shape.color = "Red"; // Set default color + new_shape.type = "Rectangle"; // Set default type to Rectangle + shapes.push_back(new_shape); // Add the new shape to the shapes container +} +``` + + + + + + +```csharp +if (MouseClicked(MouseButton.LeftButton)) +{ + ShapeModel newShape; + newShape = new ShapeModel(); + newShape.x = MouseX(); // Get the X coordinate of the mouse + newShape.y = MouseY(); // Get the Y coordinate of the mouse + newShape.width = 100; // Set default width + newShape.height = 100; // Set default height + newShape.color = "Red"; // Set default color + newShape.type = "Rectangle"; // Set default type to Rectangle + shapes.Add(newShape); // Add the new shape to the shapes container +} +``` + + + +```csharp +if (SplashKit.MouseClicked(MouseButton.LeftButton)) +{ + ShapeModel newShape; + newShape = new ShapeModel(); + newShape.x = MouseX(); // Get the X coordinate of the mouse + newShape.y = MouseY(); // Get the Y coordinate of the mouse + newShape.width = 100; // Set default width + newShape.height = 100; // Set default height + newShape.color = "Red"; // Set default color + newShape.type = "Rectangle"; // Set default type to Rectangle + shapes.Add(newShape); // Add the new shape to the shapes container +} +``` + + + + + + +```python +if mouse_clicked(left_button): + new_shape = ShapeModel() + new_shape.x = mouse_x() # Get the X coordinate of the mouse + new_shape.y = mouse_y() # Get the Y coordinate of the mouse + new_shape.width = 100 # Set default width + new_shape.height = 100 # Set default height + new_shape.color = "Red" # Set default color + new_shape.type = "Rectangle" # Set default type to Rectangle + shapes.append(new_shape) # Add the new shape to the shapes container +``` + + + +### Shape Management and Rendering +We can manage rendering by iterating through the shape collection and calling the appropriate drawing function based on the shape’s type. + + + +```cpp +clear_screen(COLOR_WHITE); + +for (int i = 0; i < shapes.size(); i++) +{ + fill_rectangle(COLOR_RED, shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height); +} + +refresh_screen(60); +``` + + + + + + +```csharp +ClearScreen(ColorWhite()); + +for (int i = 0; i < shapes.Count; i++) +{ + FillRectangle(ColorRed(), shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height); +} + +RefreshScreen(60); +``` + + + +```csharp +SplashKit.ClearScreen(Color.White); + +for (int i = 0; i < shapes.Count; i++) +{ + SplashKit.FillRectangle(Color.Red, shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height); +} + +SplashKit.RefreshScreen(60); +``` + + + + + + +```python +clear_screen(color_white()) + +for i in range(len(shapes)): + fill_rectangle( + color_red(), shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height) + +refresh_screen(60) +``` + + + +### Code example + + + +```cpp +#include "splashkit.h" +#include + +struct ShapeModel +{ + double x, y; + double width, height; + string color; + double rotation; + string type; +}; + +int main() +{ + open_window("Drawing Test", 800, 600); + vector shapes; + + while (!window_close_requested("Drawing Test")) + { + process_events(); + + if (mouse_clicked(LEFT_BUTTON)) + { + ShapeModel new_shape; + new_shape.x = mouse_x(); + new_shape.y = mouse_y(); + new_shape.width = 100; + new_shape.height = 100; + new_shape.color = "Red"; + new_shape.type = "Rectangle"; + shapes.push_back(new_shape); + } + + clear_screen(COLOR_WHITE); + + for (int i = 0; i < shapes.size(); i++) + { + fill_rectangle(COLOR_RED, shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height); + } + + refresh_screen(60); + } + close_all_windows() + return 0; +} +``` + + + + + + +```csharp +using SplashKitSDK; +using static SplashKitSDK.SplashKit; + +public struct ShapeModel +{ + public double x, y; + public double width, height; + public string color; + public double rotation; + public string type; +} + +OpenWindow("Drawing Test", 800, 600); +List shapes = new List(); + +while (!WindowCloseRequested("Drawing Test")) +{ + ProcessEvents(); + + if (MouseClicked(MouseButton.LeftButton)) + { + ShapeModel newShape = new ShapeModel(); + newShape.x = MouseX(); + newShape.y = MouseY(); + newShape.width = 100; + newShape.height = 100; + newShape.color = "Red"; + newShape.type = "Rectangle"; + shapes.Add(newShape); + } + + ClearScreen(ColorWhite()); + + for (int i = 0; i < shapes.Count; i++) + { + FillRectangle(ColorRed(), shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height); + } + + RefreshScreen(60); +} + +CloseAllWindows(); + + + +``` + + + +```csharp +using SplashKitSDK; + +namespace ShapeModelling +{ + public class Program + { + public struct ShapeModel + { + public double x, y; + public double width, height; + public string color; + public double rotation; + public string type; + } + public static void Main() + { + SplashKit.OpenWindow("Drawing Test", 800, 600); + List shapes = new List(); + + while (!SplashKit.WindowCloseRequested("Drawing Test")) + { + SplashKit.ProcessEvents(); + + if (SplashKit.MouseClicked(MouseButton.LeftButton)) + { + ShapeModel newShape = new ShapeModel(); + newShape.x = SplashKit.MouseX(); + newShape.y = SplashKit.MouseY(); + newShape.width = 100; + newShape.height = 100; + newShape.color = "Red"; + newShape.type = "Rectangle"; + shapes.Add(newShape); + } + + SplashKit.ClearScreen(Color.White); + + for (int i = 0; i < shapes.Count; i++) + { + SplashKit.FillRectangle(Color.Red, shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height); + } + + SplashKit.RefreshScreen(60); + } + + SplashKit.CloseAllWindows(); + } + } +} + +``` + + + + + + +```python +from splashkit import * + +class ShapeModel: + def __init__(self): + self.x = 0.0 + self.y = 0.0 + self.width = 0.0 + self.height = 0.0 + self.color = "" + self.rotation = 0.0 + self.type = "" + +open_window("Drawing Test", 800, 600) +shapes = [] + +while not window_close_requested_named("Drawing Test"): + process_events() + + if mouse_clicked(MouseButton.left_button): + new_shape = ShapeModel() + new_shape.x = mouse_x() + new_shape.y = mouse_y() + new_shape.width = 100 + new_shape.height = 100 + new_shape.color = "Red" + new_shape.type = "Rectangle" + shapes.append(new_shape) + + clear_screen(rgb_color(255, 255, 255)) + + for i in range(len(shapes)): + fill_rectangle(rgb_color( + 255, 0, 0), shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height) + + refresh_screen_with_target_fps(60) + +close_all_windows() + +``` + + + + +## Graphical operations +In this section, we’ll discuss how to handle the selection and movement of shapes. The code is designed to allow users to select shapes, move them around the canvas, and resize them. This approach makes the code more modular, easier to maintain, and extend. + +### Detect Shape Selection + First, we need to determine which shape the user has clicked on. This is achieved by checking if the mouse click position falls within the bounds of any existing shape. The `get_shape_at()` function is used to detect whether the mouse click is within a shape, allowing the program to identify which shape, if any, the user has selected. + + + + +```cpp +ShapeModel* get_shape_at(vector& shapes, double x, double y) +{ + for (int i = 0; i < shapes.size(); i++) + { + if (shapes[i].type == "Rectangle" && x >= shapes[i].x && x <= (shapes[i].x + shapes[i].width) && y >= shapes[i].y && y <= (shapes[i].y + shapes[i].height)) + { + return &shapes[i]; + } + } + return nullptr; +} +``` + + + + + + +```csharp +ShapeModel? GetShapeAt(List shapes, double x, double y) +{ + for (int i = 0; i < shapes.Count; i++) + { + if (shapes[i].type == "Rectangle" && x >= shapes[i].x && x <= (shapes[i].x + shapes[i].width) && y >= shapes[i].y && y <= (shapes[i].y + shapes[i].height)) + { + return shapes[i]; + } + } + return null; +} +``` + + + +```csharp +public static ShapeModel? GetShapeAt(List shapes, double x, double y) +{ + for (int i = 0; i < shapes.Count; i++) + { + if (shapes[i].type == "Rectangle" && x >= shapes[i].x && x <= (shapes[i].x + shapes[i].width) && y >= shapes[i].y && y <= (shapes[i].y + shapes[i].height)) + { + return shapes[i]; + } + } + return null; +} +``` + + + + + + +```python +def get_shape_at(shapes, x, y): + for i in range(len(shapes)): + if shapes[i].type == "Rectangle" and x >= shapes[i].x and x <= (shapes[i].x + shapes[i].width) and y >= shapes[i].y and y <= (shapes[i].y + shapes[i].height): + return shapes[i] + return None +``` + + + + +### Dragging Shapes + Once a shape is selected, the user can drag it by moving the mouse. This is done by updating the shape's x and y coordinates to follow the mouse's movement. The `is_moving` flag is used to distinguish between creating a new shape and moving an existing shape. + + + + +```cpp +if (is_moving && selected_shape != nullptr) +{ + selected_shape->x = mouse_x() - selected_shape->width / 2; + selected_shape->y = mouse_y() - selected_shape->height / 2; +} +``` + + + + + + +```csharp +if (isMoving && selectedShape != null) +{ + var s = selectedShape.Value; + s.x = MouseX() - s.width / 2; + s.y = MouseY() - s.height / 2; + for (int i = 0; i < shapes.Count; i++) + if (shapes[i].Equals(selectedShape.Value)) { shapes[i] = s; break; } + selectedShape = s; +} +``` + + + +```csharp +if (isMoving && selectedShape != null) +{ + selectedShape.x = SplashKit.MouseX() - selectedShape.width / 2; + selectedShape.y = SplashKit.MouseY() - selectedShape.height / 2; +} +``` + + + + + + +```python +if is_moving and selected_shape is not None: + selected_shape.x = mouse_x() - selected_shape.width / 2 + selected_shape.y = mouse_y() - selected_shape.height / 2 +``` + + + +### Resizing Shapes + In addition to dragging, users can resize shapes. Holding down the SPACE key while clicking a shape activates resizing mode. The shape’s width and height update dynamically based on the mouse position. + + + + +```cpp +if (is_resizing && selected_shape != nullptr) +{ + selected_shape->width = mouse_x() - selected_shape->x; + selected_shape->height = mouse_y() - selected_shape->y; +} + +``` + + + + + + +```csharp +if (isResizing && selectedShape != null) +{ + var s = selectedShape.Value; + s.width = MouseX() - s.x; + s.height = MouseY() - s.y; + for (int i = 0; i < shapes.Count; i++) + if (shapes[i].Equals(selectedShape.Value)) { shapes[i] = s; break; } + selectedShape = s; +} +``` + + + +```csharp +if (isResizing && selectedShape != null) +{ + selectedShape.width = SplashKit.MouseX() - selectedShape.x; + selectedShape.height = SplashKit.MouseY() - selectedShape.y; +} +``` + + + + + + +```python +if is_resizing and selected_shape is not None: + selected_shape.width = mouse_x() - selected_shape.x + selected_shape.height = mouse_y() - selected_shape.y +``` + + + +### Final Shape Position + When the user releases the mouse button, the dragging or resizing operation stops, and the shape's final position or size is confirmed. + +### Code example + + + +```cpp +#include "splashkit.h" +#include + +struct ShapeModel +{ + double x, y; + double width, height; + string color; + double rotation; + string type; +}; + +ShapeModel *get_shape_at(vector &shapes, double x, double y) +{ + for (int i = 0; i < shapes.size(); i++) + { + if (shapes[i].type == "Rectangle" && x >= shapes[i].x && x <= (shapes[i].x + shapes[i].width) && y >= shapes[i].y && y <= (shapes[i].y + shapes[i].height)) + { + return &shapes[i]; + } + } + return nullptr; +} + +int main() +{ + open_window("Drawing Test", 800, 600); + vector shapes; + ShapeModel *selected_shape = nullptr; + bool is_moving = false; + bool is_resizing = false; + + while (!window_close_requested("Drawing Test")) + { + process_events(); + + if (!is_moving && !is_resizing) + { + if (mouse_down(LEFT_BUTTON)) + { + selected_shape = get_shape_at(shapes, mouse_x(), mouse_y()); + if (selected_shape != nullptr) + { + if (key_down(SPACE_KEY)) + { + is_resizing = true; + } + else + { + is_moving = true; + } + } + else + { + ShapeModel new_shape; + new_shape.x = mouse_x(); + new_shape.y = mouse_y(); + new_shape.width = 100; + new_shape.height = 100; + new_shape.color = "Red"; + new_shape.type = "Rectangle"; + shapes.push_back(new_shape); + } + } + } + + if (is_moving && selected_shape != nullptr) + { + selected_shape->x = mouse_x() - selected_shape->width / 2; + selected_shape->y = mouse_y() - selected_shape->height / 2; + } + + if (is_resizing && selected_shape != nullptr) + { + selected_shape->width = mouse_x() - selected_shape->x; + selected_shape->height = mouse_y() - selected_shape->y; + } + + if (mouse_up(LEFT_BUTTON)) + { + is_moving = false; + is_resizing = false; + selected_shape = nullptr; + } + + clear_screen(COLOR_WHITE); + + for (int i = 0; i < shapes.size(); i++) + { + if (shapes[i].type == "Rectangle") + { + fill_rectangle(COLOR_RED, shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height); + } + } + + refresh_screen(60); + } + + return 0; +} +``` + + + + + + +```csharp +using System.Collections.Generic; +using SplashKitSDK; +using static SplashKitSDK.SplashKit; + +ShapeModel? GetShapeAt(List shapes, double x, double y) +{ + for (int i = 0; i < shapes.Count; i++) + { + if (shapes[i].type == "Rectangle" && x >= shapes[i].x && x <= (shapes[i].x + shapes[i].width) && y >= shapes[i].y && y <= (shapes[i].y + shapes[i].height)) + { + return shapes[i]; + } + } + return null; +} + +OpenWindow("Drawing Test", 800, 600); +List shapes = new List(); +ShapeModel? selectedShape = null; +bool isMoving = false; +bool isResizing = false; + +while (!WindowCloseRequested("Drawing Test")) +{ + ProcessEvents(); + + if (!isMoving && !isResizing) + { + if (MouseDown(MouseButton.LeftButton)) + { + selectedShape = GetShapeAt(shapes, MouseX(), MouseY()); + if (selectedShape != null) + { + if (KeyDown(KeyCode.SpaceKey)) + { + isResizing = true; + } + else + { + isMoving = true; + } + } + else + { + ShapeModel newShape = new ShapeModel(); + newShape.x = MouseX(); + newShape.y = MouseY(); + newShape.width = 100; + newShape.height = 100; + newShape.color = "Red"; + newShape.type = "Rectangle"; + shapes.Add(newShape); + } + } + } + + if (isMoving && selectedShape != null) + { + var s = selectedShape.Value; + s.x = MouseX() - s.width / 2; + s.y = MouseY() - s.height / 2; + for (int i = 0; i < shapes.Count; i++) + if (shapes[i].Equals(selectedShape.Value)) { shapes[i] = s; break; } + selectedShape = s; + } + + if (isResizing && selectedShape != null) + { + var s = selectedShape.Value; + s.width = MouseX() - s.x; + s.height = MouseY() - s.y; + for (int i = 0; i < shapes.Count; i++) + if (shapes[i].Equals(selectedShape.Value)) { shapes[i] = s; break; } + selectedShape = s; + } + + if (MouseUp(MouseButton.LeftButton)) + { + isMoving = false; + isResizing = false; + selectedShape = null; + } + + ClearScreen(ColorWhite()); + + for (int i = 0; i < shapes.Count; i++) + { + if (shapes[i].type == "Rectangle") + { + FillRectangle(ColorRed(), shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height); + } + } + + RefreshScreen(60); +} + +CloseAllWindows(); + +public struct ShapeModel +{ + public double x, y; + public double width, height; + public string color; + public double rotation; + public string type; +} + +``` + + + +```csharp +using System.Collections.Generic; +using SplashKitSDK; + +namespace ShapeModelling +{ + public class ShapeModel + { + public double x, y; + public double width, height; + public string color = ""; + public double rotation; + public string type = ""; + } + + public static class Program + { + public static ShapeModel? GetShapeAt(List shapes, double x, double y) + { + for (int i = 0; i < shapes.Count; i++) + { + if (shapes[i].type == "Rectangle" && x >= shapes[i].x && x <= (shapes[i].x + shapes[i].width) && y >= shapes[i].y && y <= (shapes[i].y + shapes[i].height)) + { + return shapes[i]; + } + } + return null; + } + + public static void Main() + { + SplashKit.OpenWindow("Drawing Test", 800, 600); + List shapes = new List(); + ShapeModel? selectedShape = null; + bool isMoving = false; + bool isResizing = false; + + while (!SplashKit.WindowCloseRequested("Drawing Test")) + { + SplashKit.ProcessEvents(); + + if (!isMoving && !isResizing) + { + if (SplashKit.MouseDown(MouseButton.LeftButton)) + { + selectedShape = GetShapeAt(shapes, SplashKit.MouseX(), SplashKit.MouseY()); + if (selectedShape != null) + { + if (SplashKit.KeyDown(KeyCode.SpaceKey)) + { + isResizing = true; + } + else + { + isMoving = true; + } + } + else + { + ShapeModel newShape = new ShapeModel(); + newShape.x = SplashKit.MouseX(); + newShape.y = SplashKit.MouseY(); + newShape.width = 100; + newShape.height = 100; + newShape.color = "Red"; + newShape.type = "Rectangle"; + shapes.Add(newShape); + } + } + } + + if (isMoving && selectedShape != null) + { + selectedShape.x = SplashKit.MouseX() - selectedShape.width / 2; + selectedShape.y = SplashKit.MouseY() - selectedShape.height / 2; + } + + if (isResizing && selectedShape != null) + { + selectedShape.width = SplashKit.MouseX() - selectedShape.x; + selectedShape.height = SplashKit.MouseY() - selectedShape.y; + } + + if (SplashKit.MouseUp(MouseButton.LeftButton)) + { + isMoving = false; + isResizing = false; + selectedShape = null; + } + + SplashKit.ClearScreen(Color.White); + + for (int i = 0; i < shapes.Count; i++) + { + if (shapes[i].type == "Rectangle") + { + SplashKit.FillRectangle(Color.Red, shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height); + } + } + + SplashKit.RefreshScreen(60); + } + + SplashKit.CloseAllWindows(); + } + } +} + +``` + + + + + + +```python +from splashkit import * + + +class ShapeModel: + def __init__(self): + self.x = 0.0 + self.y = 0.0 + self.width = 0.0 + self.height = 0.0 + self.color = "" + self.rotation = 0.0 + self.type = "" + + +def get_shape_at(shapes, x, y): + for i in range(len(shapes)): + if shapes[i].type == "Rectangle" and x >= shapes[i].x and x <= (shapes[i].x + shapes[i].width) and y >= shapes[i].y and y <= (shapes[i].y + shapes[i].height): + return shapes[i] + return None + + +open_window("Drawing Test", 800, 600) +shapes = [] +selected_shape = None +is_moving = False +is_resizing = False + +while not window_close_requested_named("Drawing Test"): + process_events() + + if not is_moving and not is_resizing: + if mouse_down(MouseButton.left_button): + selected_shape = get_shape_at(shapes, mouse_x(), mouse_y()) + if selected_shape is not None: + if key_down(KeyCode.space_key): + is_resizing = True + else: + is_moving = True + else: + new_shape = ShapeModel() + new_shape.x = mouse_x() + new_shape.y = mouse_y() + new_shape.width = 100 + new_shape.height = 100 + new_shape.color = "Red" + new_shape.type = "Rectangle" + shapes.append(new_shape) + + if is_moving and selected_shape is not None: + selected_shape.x = mouse_x() - selected_shape.width / 2 + selected_shape.y = mouse_y() - selected_shape.height / 2 + + if is_resizing and selected_shape is not None: + selected_shape.width = mouse_x() - selected_shape.x + selected_shape.height = mouse_y() - selected_shape.y + + if mouse_up(MouseButton.left_button): + is_moving = False + is_resizing = False + selected_shape = None + + clear_screen(rgb_color(255, 255, 255)) + + for i in range(len(shapes)): + if shapes[i].type == "Rectangle": + fill_rectangle(rgb_color( + 255, 0, 0), shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height) + + refresh_screen_with_target_fps(60) + +close_all_windows() + +``` + + + +## Wiring function + +In this section, we’ll add connection lines so users can visually link two shapes. Here’s how to implement it. + +In this feature, we use the `state mode` to handle the connections between shapes. State mode allows us to perform different operations in different states, such as connection mode and normal mode. Encapsulating the connection state in a class or function makes the process clearer and allows easy switching between modes. + +### Define the Shape Model +We first define a basic shape structure `ShapeModel`, which contains properties such as `x`, `y` coordinates, `width`, `height`, color, and type. + +### Mouse Interaction with Shapes +We use the `get_shape_at` function to detect whether the mouse is over a shape. It loops through the list of shapes and checks if the mouse coordinates fall within any shape's boundaries. + +### Activate Connection Mode +To start connecting shapes, we press the **C key**. This switches the program into connection mode, allowing the user to choose two shapes to connect with a line. + + + +```cpp +if (key_down(C_KEY) && !is_connecting) +{ + is_connecting = true; + connection_start = nullptr; +} +``` + + + + + + +```csharp +if (KeyDown(KeyCode.CKey) && !isConnecting) +{ + isConnecting = true; + connectionStart = null; +} +``` + + + +```csharp +if (SplashKit.KeyDown(KeyCode.CKey) && !isConnecting) +{ + isConnecting = true; + connectionStart = null; +} +``` + + + + + + +```python +if key_down(KeyCode.c_key) and not is_connecting: + is_connecting = True + connection_start = None +``` + + + +### Set Start and End Points for Connection +While in connection mode, when the user clicks on a shape, it records the starting point (`connection_start`). The next click sets the end point (`connection_end`). Once both are selected, a connection is stored. + + + + +```cpp +if (is_connecting) +{ + if (mouse_down(LEFT_BUTTON)) + { + if (connection_start == nullptr) + { + connection_start = get_shape_at(shapes, mouse_x(), mouse_y()); + } + else + { + ShapeModel* connection_end = get_shape_at(shapes, mouse_x(), mouse_y()); + if (connection_end != nullptr && connection_end != connection_start) + { + connections.push_back({ connection_start, connection_end }); + is_connecting = false; // End connection mode + connection_start = nullptr; + } + } + } +} +``` + + + + + + +```csharp +if (isConnecting) +{ + if (MouseDown(MouseButton.LeftButton)) + { + if (connectionStart == null) + { + connectionStart = GetShapeAt(shapes, MouseX(), MouseY()); + } + else + { + ShapeModel connectionEnd = GetShapeAt(shapes, MouseX(), MouseY()); + if (connectionEnd != null && connectionEnd != connectionStart) + { + Connection connection = new Connection { start = connectionStart, end = connectionEnd }; + connections.Add(connection); + isConnecting = false; + connectionStart = null; + } + } + } +} +``` + + + +```csharp +if (isConnecting) +{ + if (SplashKit.MouseDown(MouseButton.LeftButton)) + { + if (connectionStart == null) + { + connectionStart = GetShapeAt(shapes, SplashKit.MouseX(), SplashKit.MouseY()); + } + else + { + ShapeModel connectionEnd = GetShapeAt(shapes, SplashKit.MouseX(), SplashKit.MouseY()); + if (connectionEnd != null && connectionEnd != connectionStart) + { + Connection connection = new Connection { start = connectionStart, end = connectionEnd }; + connections.Add(connection); + isConnecting = false; + connectionStart = null; + } + } + } +} +``` + + + + + + +```python +if is_connecting: + if mouse_down(MouseButton.left_button): + if connection_start is None: + connection_start = get_shape_at(shapes, mouse_x(), mouse_y()) + else: + connection_end = get_shape_at(shapes, mouse_x(), mouse_y()) + if connection_end is not None and connection_end is not connection_start: + connection = Connection(connection_start, connection_end) + connections.append(connection) + is_connecting = False + connection_start = None +``` + + + +### Draw Lines between Shapes +Using the `draw_line` function, we draw lines between the center points of the two selected shapes. This line is visually updated in real time during the connection process. + + + +```cpp +for (int i = 0; i < connections.size(); i++) +{ + draw_line(COLOR_RED, connections[i].start->x + connections[i].start->width / 2, connections[i].start->y + connections[i].start->height / 2, connections[i].end->x + connections[i].end->width / 2, connections[i].end->y + connections[i].end->height / 2); +} + +``` + + + + + + +```csharp +for (int i = 0; i < connections.Count; i++) +{ + DrawLine(ColorRed(), connections[i].start.x + connections[i].start.width / 2, connections[i].start.y + connections[i].start.height / 2, connections[i].end.x + connections[i].end.width / 2, connections[i].end.y + connections[i].end.height / 2); +} +``` + + + +```csharp +for (int i = 0; i < connections.Count; i++) +{ + SplashKit.DrawLine(Color.Red, connections[i].start.x + connections[i].start.width / 2, connections[i].start.y + connections[i].start.height / 2, connections[i].end.x + connections[i].end.width / 2, connections[i].end.y + connections[i].end.height / 2); +} +``` + + + + + + +```python +for i in range(len(connections)): + draw_line(color_red(), connections[i].start.x + connections[i].start.width / 2, connections[i].start.y + + connections[i].start.height / 2, connections[i].end.x + connections[i].end.width / 2, connections[i].end.y + connections[i].end.height / 2) +``` + + + +### Exit Connection Mode +After a connection is drawn, the program exits connection mode and returns to normal interaction. + +This implementation creates an intuitive and interactive way for users to connect shapes, simulating links between objects. + +### Code example + + + +```cpp +#include "splashkit.h" +#include + +struct ShapeModel +{ + double x, y; + double width, height; + string color; + double rotation; + string type; +}; + +struct Connection +{ + ShapeModel *start; + ShapeModel *end; +}; + +ShapeModel *get_shape_at(vector &shapes, double x, double y) +{ + for (int i = 0; i < shapes.size(); i++) + { + if (shapes[i].type == "Rectangle" && x >= shapes[i].x && x <= (shapes[i].x + shapes[i].width) && y >= shapes[i].y && y <= (shapes[i].y + shapes[i].height)) + { + return &shapes[i]; + } + } + return nullptr; +} + +int main() +{ + open_window("Drawing Test", 800, 600); + vector shapes; + vector connections; + ShapeModel *selected_shape = nullptr; + ShapeModel *connection_start = nullptr; + bool is_moving = false; + bool is_resizing = false; + bool is_connecting = false; + + while (!window_close_requested("Drawing Test")) + { + process_events(); + + if (key_down(C_KEY) && !is_connecting) + { + is_connecting = true; + connection_start = nullptr; + } + + if (is_connecting) + { + if (mouse_down(LEFT_BUTTON)) + { + if (connection_start == nullptr) + { + connection_start = get_shape_at(shapes, mouse_x(), mouse_y()); + } + else + { + ShapeModel *connection_end = get_shape_at(shapes, mouse_x(), mouse_y()); + if (connection_end != nullptr && connection_end != connection_start) + { + Connection connection = {connection_start, connection_end}; + connections.push_back(connection); + is_connecting = false; + connection_start = nullptr; + } + } + } + } + else if (!is_moving && !is_resizing) + { + if (mouse_down(LEFT_BUTTON)) + { + selected_shape = get_shape_at(shapes, mouse_x(), mouse_y()); + if (selected_shape != nullptr) + { + if (key_down(SPACE_KEY)) + { + is_resizing = true; + } + else + { + is_moving = true; + } + } + else + { + ShapeModel new_shape; + new_shape.x = mouse_x(); + new_shape.y = mouse_y(); + new_shape.width = 100; + new_shape.height = 100; + new_shape.color = "Red"; + new_shape.type = "Rectangle"; + shapes.push_back(new_shape); + } + } + } + + if (is_moving && selected_shape != nullptr) + { + selected_shape->x = mouse_x() - selected_shape->width / 2; + selected_shape->y = mouse_y() - selected_shape->height / 2; + } + + if (is_resizing && selected_shape != nullptr) + { + selected_shape->width = mouse_x() - selected_shape->x; + selected_shape->height = mouse_y() - selected_shape->y; + } + + if (mouse_up(LEFT_BUTTON)) + { + is_moving = false; + is_resizing = false; + selected_shape = nullptr; + } + + clear_screen(COLOR_WHITE); + + for (int i = 0; i < shapes.size(); i++) + { + if (shapes[i].type == "Rectangle") + { + fill_rectangle(COLOR_RED, shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height); + } + } + + if (is_connecting && connection_start != nullptr) + { + draw_line(COLOR_BLACK, connection_start->x + connection_start->width / 2, connection_start->y + connection_start->height / 2, mouse_x(), mouse_y()); + } + + for (int i = 0; i < connections.size(); i++) + { + draw_line(COLOR_RED, connections[i].start->x + connections[i].start->width / 2, connections[i].start->y + connections[i].start->height / 2, connections[i].end->x + connections[i].end->width / 2, connections[i].end->y + connections[i].end->height / 2); + } + + refresh_screen(60); + } + + return 0; +} +``` + + + + + + +```csharp +using System.Collections.Generic; +using SplashKitSDK; +using static SplashKitSDK.SplashKit; + +ShapeModel GetShapeAt(List shapes, double x, double y) +{ + for (int i = 0; i < shapes.Count; i++) + { + if (shapes[i].type == "Rectangle" && x >= shapes[i].x && x <= (shapes[i].x + shapes[i].width) && y >= shapes[i].y && y <= (shapes[i].y + shapes[i].height)) + { + return shapes[i]; + } + } + return null; +} + +OpenWindow("Drawing Test", 800, 600); +List shapes = new List(); +List connections = new List(); +ShapeModel selectedShape = null; +ShapeModel connectionStart = null; +bool isMoving = false; +bool isResizing = false; +bool isConnecting = false; + +while (!WindowCloseRequested("Drawing Test")) +{ + ProcessEvents(); + + if (KeyDown(KeyCode.CKey) && !isConnecting) + { + isConnecting = true; + connectionStart = null; + } + + if (isConnecting) + { + if (MouseDown(MouseButton.LeftButton)) + { + if (connectionStart == null) + { + connectionStart = GetShapeAt(shapes, MouseX(), MouseY()); + } + else + { + ShapeModel connectionEnd = GetShapeAt(shapes, MouseX(), MouseY()); + if (connectionEnd != null && connectionEnd != connectionStart) + { + Connection connection = new Connection { start = connectionStart, end = connectionEnd }; + connections.Add(connection); + isConnecting = false; + connectionStart = null; + } + } + } + } + else if (!isMoving && !isResizing) + { + if (MouseDown(MouseButton.LeftButton)) + { + selectedShape = GetShapeAt(shapes, MouseX(), MouseY()); + if (selectedShape != null) + { + if (KeyDown(KeyCode.SpaceKey)) + { + isResizing = true; + } + else + { + isMoving = true; + } + } + else + { + ShapeModel newShape = new ShapeModel(); + newShape.x = MouseX(); + newShape.y = MouseY(); + newShape.width = 100; + newShape.height = 100; + newShape.color = "Red"; + newShape.type = "Rectangle"; + shapes.Add(newShape); + } + } + } + + if (isMoving && selectedShape != null) + { + selectedShape.x = MouseX() - selectedShape.width / 2; + selectedShape.y = MouseY() - selectedShape.height / 2; + } + + if (isResizing && selectedShape != null) + { + selectedShape.width = MouseX() - selectedShape.x; + selectedShape.height = MouseY() - selectedShape.y; + } + + if (MouseUp(MouseButton.LeftButton)) + { + isMoving = false; + isResizing = false; + selectedShape = null; + } + + ClearScreen(ColorWhite()); + + for (int i = 0; i < shapes.Count; i++) + { + if (shapes[i].type == "Rectangle") + { + FillRectangle(ColorRed(), shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height); + } + } + + if (isConnecting && connectionStart != null) + { + DrawLine(ColorBlack(), connectionStart.x + connectionStart.width / 2, connectionStart.y + connectionStart.height / 2, MouseX(), MouseY()); + } + + for (int i = 0; i < connections.Count; i++) + { + DrawLine(ColorRed(), connections[i].start.x + connections[i].start.width / 2, connections[i].start.y + connections[i].start.height / 2, connections[i].end.x + connections[i].end.width / 2, connections[i].end.y + connections[i].end.height / 2); + } + + RefreshScreen(60); +} + +public class ShapeModel +{ + public double x, y; + public double width, height; + public string color; + public double rotation; + public string type; +} + +public class Connection +{ + public ShapeModel start; + public ShapeModel end; +} + +``` + + + +```csharp +using System.Collections.Generic; +using SplashKitSDK; + +namespace ShapeModelling +{ + public class ShapeModel + { + public double x, y; + public double width, height; + public string color; + public double rotation; + public string type; + } + + public class Connection + { + public ShapeModel start; + public ShapeModel end; + } + + public static class Program + { + public static ShapeModel GetShapeAt(List shapes, double x, double y) + { + for (int i = 0; i < shapes.Count; i++) + { + if (shapes[i].type == "Rectangle" && x >= shapes[i].x && x <= (shapes[i].x + shapes[i].width) && y >= shapes[i].y && y <= (shapes[i].y + shapes[i].height)) + { + return shapes[i]; + } + } + return null; + } + + public static void Main() + { + SplashKit.OpenWindow("Drawing Test", 800, 600); + List shapes = new List(); + List connections = new List(); + ShapeModel selectedShape = null; + ShapeModel connectionStart = null; + bool isMoving = false; + bool isResizing = false; + bool isConnecting = false; + + while (!SplashKit.WindowCloseRequested("Drawing Test")) + { + SplashKit.ProcessEvents(); + + if (SplashKit.KeyDown(KeyCode.CKey) && !isConnecting) + { + isConnecting = true; + connectionStart = null; + } + + if (isConnecting) + { + if (SplashKit.MouseDown(MouseButton.LeftButton)) + { + if (connectionStart == null) + { + connectionStart = GetShapeAt(shapes, SplashKit.MouseX(), SplashKit.MouseY()); + } + else + { + ShapeModel connectionEnd = GetShapeAt(shapes, SplashKit.MouseX(), SplashKit.MouseY()); + if (connectionEnd != null && connectionEnd != connectionStart) + { + Connection connection = new Connection { start = connectionStart, end = connectionEnd }; + connections.Add(connection); + isConnecting = false; + connectionStart = null; + } + } + } + } + else if (!isMoving && !isResizing) + { + if (SplashKit.MouseDown(MouseButton.LeftButton)) + { + selectedShape = GetShapeAt(shapes, SplashKit.MouseX(), SplashKit.MouseY()); + if (selectedShape != null) + { + if (SplashKit.KeyDown(KeyCode.SpaceKey)) + { + isResizing = true; + } + else + { + isMoving = true; + } + } + else + { + ShapeModel newShape = new ShapeModel(); + newShape.x = SplashKit.MouseX(); + newShape.y = SplashKit.MouseY(); + newShape.width = 100; + newShape.height = 100; + newShape.color = "Red"; + newShape.type = "Rectangle"; + shapes.Add(newShape); + } + } + } + + if (isMoving && selectedShape != null) + { + selectedShape.x = SplashKit.MouseX() - selectedShape.width / 2; + selectedShape.y = SplashKit.MouseY() - selectedShape.height / 2; + } + + if (isResizing && selectedShape != null) + { + selectedShape.width = SplashKit.MouseX() - selectedShape.x; + selectedShape.height = SplashKit.MouseY() - selectedShape.y; + } + + if (SplashKit.MouseUp(MouseButton.LeftButton)) + { + isMoving = false; + isResizing = false; + selectedShape = null; + } + + SplashKit.ClearScreen(Color.White); + + for (int i = 0; i < shapes.Count; i++) + { + if (shapes[i].type == "Rectangle") + { + SplashKit.FillRectangle(Color.Red, shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height); + } + } + + if (isConnecting && connectionStart != null) + { + SplashKit.DrawLine(Color.Black, connectionStart.x + connectionStart.width / 2, connectionStart.y + connectionStart.height / 2, SplashKit.MouseX(), SplashKit.MouseY()); + } + + for (int i = 0; i < connections.Count; i++) + { + SplashKit.DrawLine(Color.Red, connections[i].start.x + connections[i].start.width / 2, connections[i].start.y + connections[i].start.height / 2, connections[i].end.x + connections[i].end.width / 2, connections[i].end.y + connections[i].end.height / 2); + } + + SplashKit.RefreshScreen(60); + } + } + } +} + +``` + + + + + + +```python +from splashkit import * + + +class ShapeModel: + def __init__(self): + self.x = 0.0 + self.y = 0.0 + self.width = 0.0 + self.height = 0.0 + self.color = "" + self.rotation = 0.0 + self.type = "" + + +class Connection: + def __init__(self, start, end): + self.start = start + self.end = end + + +def get_shape_at(shapes, x, y): + for i in range(len(shapes)): + if shapes[i].type == "Rectangle" and x >= shapes[i].x and x <= (shapes[i].x + shapes[i].width) and y >= shapes[i].y and y <= (shapes[i].y + shapes[i].height): + return shapes[i] + return None + + +open_window("Drawing Test", 800, 600) +shapes = [] +connections = [] +selected_shape = None +connection_start = None +is_moving = False +is_resizing = False +is_connecting = False + +while not window_close_requested_named("Drawing Test"): + process_events() + + if key_down(KeyCode.c_key) and not is_connecting: + is_connecting = True + connection_start = None + + if is_connecting: + if mouse_down(MouseButton.left_button): + if connection_start is None: + connection_start = get_shape_at(shapes, mouse_x(), mouse_y()) + else: + connection_end = get_shape_at(shapes, mouse_x(), mouse_y()) + if connection_end is not None and connection_end is not connection_start: + connection = Connection(connection_start, connection_end) + connections.append(connection) + is_connecting = False + connection_start = None + elif not is_moving and not is_resizing: + if mouse_down(MouseButton.left_button): + selected_shape = get_shape_at(shapes, mouse_x(), mouse_y()) + if selected_shape is not None: + if key_down(KeyCode.space_key): + is_resizing = True + else: + is_moving = True + else: + new_shape = ShapeModel() + new_shape.x = mouse_x() + new_shape.y = mouse_y() + new_shape.width = 100 + new_shape.height = 100 + new_shape.color = "Red" + new_shape.type = "Rectangle" + shapes.append(new_shape) + + if is_moving and selected_shape is not None: + selected_shape.x = mouse_x() - selected_shape.width / 2 + selected_shape.y = mouse_y() - selected_shape.height / 2 + + if is_resizing and selected_shape is not None: + selected_shape.width = mouse_x() - selected_shape.x + selected_shape.height = mouse_y() - selected_shape.y + + if mouse_up(MouseButton.left_button): + is_moving = False + is_resizing = False + selected_shape = None + + clear_screen(color_white()) + + for i in range(len(shapes)): + if shapes[i].type == "Rectangle": + fill_rectangle(color_red(), shapes[i].x, shapes[i].y, shapes[i].width, shapes[i].height) + + if is_connecting and connection_start is not None: + draw_line(color_black(), connection_start.x + connection_start.width / 2, + connection_start.y + connection_start.height / 2, mouse_x(), mouse_y()) + + for i in range(len(connections)): + draw_line(color_red(), connections[i].start.x + connections[i].start.width / 2, connections[i].start.y + + connections[i].start.height / 2, connections[i].end.x + connections[i].end.width / 2, connections[i].end.y + connections[i].end.height / 2) + + refresh_screen_with_target_fps(60) + +``` + + + + diff --git a/src/content/docs/guides/graphics/beginner-game-tutorial.mdx b/src/content/docs/guides/graphics/beginner-game-tutorial.mdx new file mode 100644 index 000000000..17bdf0fbf --- /dev/null +++ b/src/content/docs/guides/graphics/beginner-game-tutorial.mdx @@ -0,0 +1,300 @@ +--- +title: "Beginner Game Tutorial: Flappy Bird" +description: "Learn to create your first game with SplashKit! This step-by-step tutorial will guide you through building a Flappy Bird remake from scratch." +author: "Vishnu Vengadeswaran" +lastupdated: "September 2025" +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +**{frontmatter.description}** +Written by: {frontmatter.author} +_Last updated: {frontmatter.lastupdated}_ + +--- + +## Introduction + +In this tutorial, we'll build a beginner friendly Flappy Bird remake using SplashKit. By the end of this tutorial, you'll have a working game that you can play and share with friends. Our Flappy Bird game will have a yellow bird that falls with gravity, green pipes that scroll from right to left, score tracking when you pass through pipes, and a game over screen when you hit pipes or the ground. The controls are simple - just press SPACE to make the bird jump! +![Flappy Bird Program Screenshot](./images/beginner_game/flappy.png) +:::note +You can download and play the game in this tutorial using the complete code file here: +[**Download Code**](/resources/guides/graphics/basics/BeginnerGameTutorialCode.zip) +::: + + +## Step 1: Setting Up Our Game Window + +First, let's create our game window and define the basic constants we'll need throughout our game. + + + + +```cpp +#include "splashkit.h" + +// Game window size +const int WINDOW_WIDTH = 400; +const int WINDOW_HEIGHT = 600; + +// Bird properties +const int BIRD_X = 100; +const int BIRD_SIZE = 20; +const double GRAVITY = 0.5; +const double JUMP_STRENGTH = -8; + +// Pipe properties +const int PIPE_WIDTH = 60; +const int PIPE_GAP = 150; +const double PIPE_SPEED = 2.0; +const int PIPE_SPACING = 250; + +// Font for text +font game_font; +``` + + + + +**What's happening here?** +We include SplashKit to access all our game functions, define constants for our window size (400x600 pixels), set up properties for our bird including where it sits horizontally, how big it is, how gravity affects it, and how strong its jump is. We also define pipe properties like width, gap size, how fast they move, and how far apart they are. + +## Step 2: Creating Our Game State Structure + +Every game needs to keep track of information. Let's create a structure to hold all our game data in one organized place. + + + + +```cpp +// Game state structure +struct GameState { + // Bird position and movement + double bird_y; + double bird_velocity; + + // Pipe positions (x, gap_y) + double pipe1_x, pipe1_gap_y; + double pipe2_x, pipe2_gap_y; + + // Game status + int score; + bool game_over; + bool started; + bool pipe1_passed; + bool pipe2_passed; + + // Constructor + GameState() { + bird_y = WINDOW_HEIGHT / 2; + bird_velocity = 0; + pipe1_x = WINDOW_WIDTH; + pipe1_gap_y = WINDOW_HEIGHT / 2; + pipe2_x = WINDOW_WIDTH + PIPE_SPACING; + pipe2_gap_y = WINDOW_HEIGHT / 2; + score = 0; + game_over = false; + started = false; + pipe1_passed = false; + pipe2_passed = false; + } +}; +``` + + + +**Why do we need this?** +The `bird_y` variable tracks where our bird is vertically on the screen, while `bird_velocity` tracks how fast and in what direction our bird is moving. Since we have two pipes that scroll across the screen, we track their x position and where their gap is. The `score` keeps track of how many pipes we've passed, and `game_over` and `started` help us manage the game's different states. + +## Step 3: Handling Player Input + +Now let's make our game respond to player input. We'll handle the SPACE key for jumping and restarting. + + + + +```cpp +// Handle input +void handle_input(GameState& game) { + if (key_typed(SPACE_KEY)) { + if (!game.started) { + game.started = true; + } + if (!game.game_over) { + game.bird_velocity = JUMP_STRENGTH; + } + } + + if (key_typed(SPACE_KEY) && game.game_over) { + // Reset game + game.bird_y = WINDOW_HEIGHT / 2; + game.bird_velocity = 0; + game.pipe1_x = WINDOW_WIDTH; + game.pipe1_gap_y = WINDOW_HEIGHT / 2; + game.pipe2_x = WINDOW_WIDTH + PIPE_SPACING; + game.pipe2_gap_y = WINDOW_HEIGHT / 2; + game.score = 0; + game.game_over = false; + game.started = false; + game.pipe1_passed = false; + game.pipe2_passed = false; + } +} +``` + + + +**What's happening here?** +When SPACE is pressed and the game hasn't started yet, we start the game. When SPACE is pressed during gameplay, we make the bird jump by setting its velocity to a negative value (upward). When SPACE is pressed after game over, we reset everything back to the beginning. + +## Step 4: Updating Game Logic + +This is where the magic happens! We'll update our bird's position, move the pipes, and check for collisions. + + + + +```cpp +// Update game logic +void update_game(GameState& game) { + if (!game.started || game.game_over) { + return; + } + + // Apply gravity to bird + game.bird_velocity += GRAVITY; + game.bird_y += game.bird_velocity; + + // Move pipes + game.pipe1_x -= PIPE_SPEED; + game.pipe2_x -= PIPE_SPEED; + + // Reset pipes when they go off screen + if (game.pipe1_x < -PIPE_WIDTH) { + game.pipe1_x = WINDOW_WIDTH; + game.pipe1_gap_y = 150 + rnd() * (WINDOW_HEIGHT - 300); + game.pipe1_passed = false; + } + if (game.pipe2_x < -PIPE_WIDTH) { + game.pipe2_x = WINDOW_WIDTH; + game.pipe2_gap_y = 150 + rnd() * (WINDOW_HEIGHT - 300); + game.pipe2_passed = false; + } + + // Increase score when a pipe passes the bird's x position + if (!game.pipe1_passed && game.pipe1_x + PIPE_WIDTH < BIRD_X) { + game.score++; + game.pipe1_passed = true; + } + if (!game.pipe2_passed && game.pipe2_x + PIPE_WIDTH < BIRD_X) { + game.score++; + game.pipe2_passed = true; + } + + // Check collisions + // Ground and ceiling + if (game.bird_y < 0 || game.bird_y > WINDOW_HEIGHT - BIRD_SIZE) { + game.game_over = true; + } + + // Pipe 1 collision + if (game.bird_y + BIRD_SIZE > game.pipe1_gap_y + PIPE_GAP/2 || + game.bird_y < game.pipe1_gap_y - PIPE_GAP/2) { + if (BIRD_X + BIRD_SIZE > game.pipe1_x && + BIRD_X < game.pipe1_x + PIPE_WIDTH) { + game.game_over = true; + } + } + + // Pipe 2 collision + if (game.bird_y + BIRD_SIZE > game.pipe2_gap_y + PIPE_GAP/2 || + game.bird_y < game.pipe2_gap_y - PIPE_GAP/2) { + if (BIRD_X + BIRD_SIZE > game.pipe2_x && + BIRD_X < game.pipe2_x + PIPE_WIDTH) { + game.game_over = true; + } + } +} +``` + + + +**Let's break this down:** +First, we check if the game is running (started and not game over). We apply gravity by adding to the bird's velocity, then move the bird. We move both pipes to the left by subtracting the pipe speed. When pipes go off screen, we reset them to the right side with a random gap position and increase the score when a pipe is passed. We check if the bird hits the top or bottom of the screen, and we check if the bird hits either pipe by seeing if it's in the wrong vertical position when it's horizontally aligned with a pipe. + +## Step 5: Drawing Everything + +Now let's make our game visible! We'll draw the bird, pipes, score, and messages. + + + + +```cpp +// Draw everything +void draw_game(const GameState& game) { + clear_screen(COLOR_SKY_BLUE); + + // Draw pipes + // Pipe 1 + fill_rectangle(COLOR_GREEN, game.pipe1_x, 0, PIPE_WIDTH, game.pipe1_gap_y - PIPE_GAP/2); + fill_rectangle(COLOR_GREEN, game.pipe1_x, game.pipe1_gap_y + PIPE_GAP/2, PIPE_WIDTH, WINDOW_HEIGHT); + + // Pipe 2 + fill_rectangle(COLOR_GREEN, game.pipe2_x, 0, PIPE_WIDTH, game.pipe2_gap_y - PIPE_GAP/2); + fill_rectangle(COLOR_GREEN, game.pipe2_x, game.pipe2_gap_y + PIPE_GAP/2, PIPE_WIDTH, WINDOW_HEIGHT); + + // Draw bird + fill_circle(COLOR_YELLOW, BIRD_X, game.bird_y, BIRD_SIZE); + + // Draw score + draw_text("Score: " + std::to_string(game.score), COLOR_WHITE, game_font, 24, 10, 10); + + // Draw instructions + if (!game.started) { + draw_text("Press SPACE to start", COLOR_WHITE, game_font, 18, WINDOW_WIDTH/2 - 75, WINDOW_HEIGHT/2); + } else if (game.game_over) { + draw_text("GAME OVER!", COLOR_RED, game_font, 32, WINDOW_WIDTH/2 - 85, WINDOW_HEIGHT/2 - 50); + draw_text("Press Space to restart", COLOR_WHITE, game_font, 18, WINDOW_WIDTH/2 - 85, WINDOW_HEIGHT/2); + } +} +``` + + + +**What we're drawing:** +We're drawing a sky blue background with [Clear Screen](/api/graphics/#clear-screen), two green pipes for each pipe (top and bottom sections with a gap in between) using [Fill Rectangle](/api/graphics/#fill-rectangle), a yellow circle for our bird with [Fill Circle](/api/graphics/#fill-circle), the current score in white text with [Draw Text](/api/graphics/#draw-text-no-font-no-size), and instructions that change based on the game state. + +## Step 6: The Main Game Loop + +Finally, let's put it all together in our main function that runs the entire game. + + + + +```cpp +// Main function +int main() { + open_window("Flappy Bird", WINDOW_WIDTH, WINDOW_HEIGHT); + + // Load font + game_font = load_font("GameFont", "DMSerifText.ttf"); + + GameState game; + + while (!quit_requested()) { + process_events(); + handle_input(game); + update_game(game); + draw_game(game); + refresh_screen(60); + } + + close_all_windows(); + return 0; +} +``` + + + +**The game loop explained:** +We open our game window, create our game state, and run a loop that continues until the player closes the window. Each frame that passes, we process input using [Process Events](/api/input/#process-events), and handle player inputs using the created functions, update game logic, and refresh the screen at 60 FPS. When the loop ends, we close the window. \ No newline at end of file diff --git a/src/content/docs/guides/graphics/images/beginner_game/flappy.png b/src/content/docs/guides/graphics/images/beginner_game/flappy.png new file mode 100644 index 000000000..6819e977e Binary files /dev/null and b/src/content/docs/guides/graphics/images/beginner_game/flappy.png differ diff --git a/src/content/docs/guides/index.mdx b/src/content/docs/guides/index.mdx index c01f14047..485a129b0 100644 --- a/src/content/docs/guides/index.mdx +++ b/src/content/docs/guides/index.mdx @@ -60,6 +60,10 @@ SplashKit provides a versatile set of categories, encompassing graphics, audio, title="SplashKit Colors" href="/guides/color/splashkit-colors/" /> + ### Raspberry Pi GPIO