Scythe
SCYTHE is a lightweight, C#-based game engine focused on modifiability and rapid iteration using Raylib.
A powerful resolution-handler and rendering library for LÖVE 📐
A resolution-handling and rendering library for LÖVE
Shöve is a powerful, flexible resolution-handling and rendering library for the LÖVE framework. Using Shöve, you can develop your game at a fixed resolution while scaling to fit the window or screen - all with a simple, intuitive API.
Shöve takes the dev branch of push, redesigns the API, and builds on its Canvas rendering concept to create a powerful and intuitive library that can handle complex rendering scenarios.
Shöve offers a progressive learning curve—start simple and add complexity as needed 🧑🎓
Here's a basic example to get started with Shöve.
shove = require("shove")
function love.load()
-- Initialize Shöve with fixed game resolution and options
shove.setResolution(400, 300, {fitMethod = "aspect"})
-- Set up a resizable window
shove.setWindowMode(800, 600, {resizable = true})
end
function love.draw()
shove.beginDraw()
-- Your game here!
love.graphics.clear(0.1, 0.1, 0.2)
love.graphics.setColor(0.918, 0.059, 0.573)
love.graphics.rectangle("fill", 150 + math.sin(love.timer.getTime()) * 150, 100, 100, 100)
shove.endDraw()
end
function love.keypressed(key)
if key == "escape" then love.event.quit() end
end
You can now draw your game at a fixed resolution and have it scale to fit the window.
💡 NOTE! That is all you need to get started! Everything else that follows is optional, but very tasty 👅
Run love demo/ to explore all demos. Use SPACE to cycle through examples, f to toggle fullscreen, and ESC to exit.
Each demo showcases different Shöve capabilities:
The demos serve as practical examples that showcase Shöve's features in action, providing developers with working code that demonstrates how to implement various rendering techniques like custom canvas integration, layer management, and visual effects.
This guide provides documentation for using Shöve.
Place shove.lua in your project directory and require it in your code:
shove = require("shove")
Shöve provides a system for rendering your game at a fixed resolution while scaling to fit to the window or screen.
Shöve offers several methods to fit your game to different screen sizes:
-- Use pixel-perfect scaling
shove.setResolution(320, 240, {fitMethod = "pixel"})
The scalingFilter option determines how textures are scaled when rendering your game. Here's how it works:
You can set scalingFilter in two ways:
At initialization:
shove.setResolution(800, 600, {
fitMethod = "aspect",
scalingFilter = "nearest" -- Set filtering explicitly
})
At runtime:
shove.setScalingFilter("linear")
If scalingFilter is not explicitly specified, Shöve automatically selects a default based on your fitMethod:
With fitMethod = "pixel" it defaults to "nearest" filtering to preserve pixel-perfect appearance. With all other fit methods ("aspect", "stretch", "none") it defaults to "linear" filtering for smoother scaling.
nearest: Nearest-neighbor filtering (sharp, pixel-perfect, no interpolation)linear: Linear filtering (smooth blending between pixels)Here are the typical use cases:
"nearest" when creating pixel art games where you want to preserve the crisp edges of your pixels"linear" for smoother visuals with games that use higher resolution assetsYou can check the current setting at any time with shove.getScalingFilter().
Shöve provides wrapper functions for LÖVE's window management.
Set the window dimensions and properties with automatic resize handling using shove.setWindowMode(width, height, flags).
💡 NOTE: For best results, call shove.setResolution() before using these window management functions to ensure proper viewport initialization.
-- Create a window half the size of the desktop
local desktopWidth, desktopHeight = love.window.getDesktopDimensions()
shove.setWindowMode(desktopWidth * 0.5, desktopHeight * 0.5, {
resizable = true,
vsync = true,
minwidth = 400,
minheight = 300
})
Use shove.updateWindowMode(width, height, flags) to change the window size and properties.
Direct rendering is simple and lightweight. It's suitable for games that don't need advanced rendering features.
💡NOTE! With direct rendering enabled, none of the layer rendering or effects functions are available.
-- Initialize with direct rendering mode
shove.setResolution(960, 540, {renderMode = "direct"})
function love.draw()
shove.beginDraw()
-- All drawing operations are directly scaled and positioned
love.graphics.setColor(1, 0, 0)
love.graphics.rectangle("fill", 100, 100, 200, 200)
-- Drawing happens on a single surface
love.graphics.setColor(0, 0, 1)
love.graphics.circle("fill", 400, 300, 50)
shove.endDraw()
end
Think of Shöve's layers like Photoshop or Figma layers—separate "sheets" that combine to create your complete scene.
-- Traditional approach (harder to manage) function love.draw() drawBackground() drawCharacters() drawParticles() drawUI() if debugMode then drawDebugInfo() end end
With Shöve's layers, you can organize these logically and manage them independently:
-- Layer approach (more flexible and organized)
shove.beginDraw()
shove.beginLayer("background")
drawBackground()
shove.endLayer()
shove.beginLayer("gameplay")
drawCharacters()
drawParticles()
shove.endLayer()
shove.beginLayer("ui")
drawUI()
shove.endLayer()
-- Only rendered when debug mode is on
shove.beginLayer("debug")
if debugMode then
shove.showLayer("debug")
drawDebugInfo()
else
shove.hideLayer("debug")
end
shove.endLayer()
shove.endDraw()
Many of the benefits of Shöve's layers are similar to those in professional creative software:
Layer rendering provides powerful features for organizing your rendering into separate layers that can be manipulated independently. Under the hood, Shöve uses LÖVE's Canvas system to achieve this, but hides the complexity behind a simple API.
shove = require("shove")
-- Initialize with layer rendering mode
shove.setResolution(800, 600, {renderMode = "layer"})
function love.load()
-- Create some layers (optional, they're created automatically when used)
shove.createLayer("background")
shove.createLayer("entities")
shove.createLayer("ui", {zIndex = 10}) -- Higher zIndex renders on top
end
function love.draw()
shove.beginDraw()
-- Draw to the background layer
shove.beginLayer("background")
love.graphics.setColor(0.2, 0.3, 0.8)
love.graphics.rectangle("fill", 0, 0, 800, 600)
shove.endLayer()
-- Draw to the entities layer
shove.beginLayer("entities")
love.graphics.setColor(1, 1, 1)
love.graphics.circle("fill", 400, 300, 50)
shove.endLayer()
-- Draw to the UI layer
shove.beginLayer("ui")
love.graphics.setColor(1, 0.8, 0)
love.graphics.print("Score: 100", 20, 20)
shove.endLayer()
shove.endDraw()
end
Shöve automatically manages canvas states to ensure your drawing operations work correctly with both Shöve's rendering pipeline and your own custom canvas operations.
When you call shove.beginDraw(), Shöve temporarily wraps love.graphics.setCanvas() until you call shove.endDraw().
This means:
love.graphics.setCanvas() inside the Shöve draw cycle to render to your own canvases.shove.endDraw(), love.graphics.setCanvas() is restored to its original behavior.This feature allows you to:
function love.draw()
-- Begin Shöve's drawing context
shove.beginDraw()
-- Draw a red circle in the Shöve managed area
love.graphics.setColor(1, 0, 0)
love.graphics.circle("fill", 200, 150, 100)
-- User manually changes canvas during Shöve's drawing cycle
love.graphics.setCanvas(userCanvas)
love.graphics.clear(0, 0, 0, 0)
love.graphics.setColor(0, 1, 0)
love.graphics.rectangle("fill", 50, 50, 100, 100)
love.graphics.setCanvas() -- Reset to default
-- Continue drawing in Shöve's context with transforms preserved
love.graphics.setColor(0, 0, 1)
love.graphics.circle("fill", 200, 150, 50)
-- End Shöve's drawing context
shove.endDraw()
-- Draw your custom canvas outside Shöve's context if desired
love.graphics.setColor(1, 1, 1)
love.graphics.draw(userCanvas, 580, 10)
end
direct render mode, transforms are preserved when switching back from user canvases.layer render mode, the active layer is restored when switching back from user canvases.Shöve provides functions to convert between screen and game viewport coordinates:
function love.mousepressed(screenX, screenY, button)
-- Convert screen coordinates to viewport coordinates
local inViewport, gameX, gameY = shove.screenToViewport(screenX, screenY)
if inViewport then
-- Mouse is inside the game viewport
handleClick(gameX, gameY, button)
end
end
-- Get mouse position directly in viewport coordinates
function love.update(dt)
local mouseInViewport, mouseX, mouseY = shove.mouseToViewport()
if inside then
player:aimToward(mouseX, mouseY)
end
end
-- Convert viewport coordinates back to screen coordinates
function drawScreenUI()
local screenX, screenY = shove.viewportToScreen(playerX, playerY)
-- Draw something at the screen position
end
While Shöve automatically creates layers when you first draw to them after declaring them with beginLayer(), there are several compelling reasons to manually create layers with createLayer() instead:
-- Create a layer with specific properties
shove.createLayer("ui", {
zIndex = 100, -- Ensure UI is always on top
visible = false, -- Start hidden until needed
stencil = true -- Enable stencil support
})
Manual creation lets you configure layers with specific options from the start, rather than using defaults and modifying later. Pre-defining your layers creates a clear "blueprint" of your rendering architecture:
function initLayers()
-- Background layers
shove.createLayer("sky", {zIndex = 10})
shove.createLayer("mountains", {zIndex = 20})
shove.createLayer("clouds", {zIndex = 25})
-- Gameplay layers
shove.createLayer("terrain", {zIndex = 30})
shove.createLayer("entities", {zIndex = 40})
shove.createLayer("particles", {zIndex = 50})
-- UI layers
shove.createLayer("hud", {zIndex = 100})
shove.createLayer("menu", {zIndex = 110})
shove.createLayer("debug", {zIndex = 1000, visible = debugMode})
end
This approach documents your rendering pipeline and makes relationships between layers clear. Manual creation allows you to configure layer relationships before any drawing occurs:
-- Set up mask relationships at initialization
shove.createLayer("lightning_mask", {stencil = true})
shove.createLayer("foreground")
shove.setLayerMask("foreground", "lightning_mask")
-- Apply initial effects
shove.createLayer("underwater")
shove.addEffect("underwater", waterDistortionShader)
Creating all layers upfront improves predictability:
function love.load()
-- Game setup
setupEntities()
loadResources()
-- Define our rendering architecture upfront
shove.createLayer("background", {zIndex = 10})
shove.createLayer("middleground", {zIndex = 20})
shove.createLayer("entities", {zIndex = 30})
shove.createLayer("particles", {zIndex = 40})
shove.createLayer("ui", {zIndex = 100})
-- Configure special properties
shove.addEffect("background", parallaxEffect)
shove.createLayer("mask_layer", {stencil = true})
shove.setLayerMask("particles", "mask_layer")
end
With this approach, your rendering architecture is clearly defined, properly configured, and ready to use before your first frame is drawn.
Shove provides full support for LÖVE's blend modes at the layer level. This gives you precise control over how layers blend with each other when composited.
For convenience and better code readability, Shove provides constants for all available blend modes:
-- Use constants instead of string literals
shove.setLayerBlendMode("particles", shove.BLEND.ADD)
-- Available blend mode constants
shove.BLEND.ALPHA -- Normal alpha blending (default)
shove.BLEND.REPLACE -- Replace pixels without blending
shove.BLEND.SCREEN -- Screen blending (lightens)
shove.BLEND.ADD -- Additive blending (glow effects)
shove.BLEND.SUBTRACT -- Subtractive blending
shove.BLEND.MULTIPLY -- Multiply colors (darkening)
shove.BLEND.LIGHTEN -- Keep lighter colors
shove.BLEND.DARKEN -- Keep darker colors
-- Alpha mode constants
shove.ALPHA.MULTIPLY -- Standard alpha multiplication (default)
shove.ALPHA.PREMULTIPLIED -- For pre-multiplied alpha content
You can set blend modes either during layer creation or at any time afterward:
-- Set blend mode during layer creation
shove.createLayer("glow", {
zIndex = 50,
blendMode = shove.BLEND.ADD, -- Additive blending
blendAlphaMode = shove.ALPHA.MULTIPLY -- Default alpha mode
})
-- Set blend mode for an existing layer
shove.setLayerBlendMode("particles", shove.BLEND.ADD)
shove.setLayerBlendMode("ui", shove.BLEND.ALPHA, shove.ALPHA.PREMULTIPLIED)
-- Get current blend modes
local blendMode, alphaMode = shove.getLayerBlendMode("particles")
Different blend modes enable various visual effects:
shove.setLayerBlendMode("fire", shove.BLEND.ADD)
shove.setLayerBlendMode("shadow", shove.BLEND.MULTIPLY)
shove.setLayerBlendMode("lightning", shove.BLEND.SCREEN)
shove.setLayerBlendMode("ui", shove.BLEND.ALPHA)
For proper rendering of content drawn to canvases, Shöve automatically uses premultiplied alpha when compositing layers, while respecting each layer's blend mode setting.
Layer masking in Shöve provides a straightforward way to control visibility between layers. The masking system uses one layer's content to determine which parts of another layer are visible.
Behind the scenes, Shöve's layer masking system works through these steps:
shove.setLayerMask("targetLayer", "maskLayer") function assigns the relationshipBehind the scenes, Shöve uses LÖVE's stencil system and automatically manages the stencil buffer and shader masks for you.
-- Create a mask layer
shove.beginDraw()
shove.beginLayer("mask")
-- Draw shapes to define the visible area
love.graphics.circle("fill", 400, 300, 100)
shove.endLayer()
-- Set the mask
shove.setLayerMask("content", "mask")
-- Draw content that will be masked
shove.beginLayer("content")
-- This will only be visible inside the circle
drawComplexScene()
shove.endLayer()
shove.endDraw()
Shöve's layer masking offers an elegant abstraction over LÖVE's stencil buffer, trading some low-level flexibility for ease of use and integration with the layer-based rendering architecture.
shove.setLayerMask("content", "mask")
versus
love.graphics.stencil(stencilFunction, "replace", 1)
love.graphics.setStencilTest("greater", 0)
-- Draw content
love.graphics.setStencilTest()
Although layer masks provide a high-level API for masking, there are scenarios where manual stencil buffer manipulation might be more appropriate and Shöve supports direct access to the stencil buffer for advanced use cases.
The layer mask approach separates the mask definition from its application, resulting in more modular, maintainable code that follows a declarative programming style. The stencil approach gives more immediate control but requires more technical knowledge and careful state management.
Shöve includes a powerful effect system for applying Shaders to layers or the final output.
The effect system is designed to be efficient by:
💡NOTE! Each additional effect requires more GPU processing. Complex shaders or many effects can impact performance.
Layer effects provide a powerful way to apply shader-based visual effects to specific layers independently. This creates a flexible rendering pipeline where different parts of your scene can have unique visual treatments.
Layer effects provide a clean abstraction over LÖVE's shader system that integrates with the layer-based rendering architecture, giving you powerful visual capabilities with a simple API. Here's how it works:
effects tableeffects table for the layerDuring the rendering process, here's what happens:
beginLayer() is called, Shöve sets the current active layerbeginLayer() and endLayer() are captured on the layer's canvasendLayer() is called, Shöve checks if the layer has any effectsEach effect's shader transforms the entire layer canvas, not individual drawing operations. This means that all content drawn to a layer is processed together by its effects.
-- Create some shaders
local blurShader = love.graphics.newShader("blur.glsl")
local waveShader = love.graphics.newShader("wave.glsl")
-- Add effects to specific layers
shove.addEffect("water", waveShader)
shove.addEffect("background", blurShader)
-- Remove an effect
shove.removeEffect("background", blurShader)
-- Clear all effects from a layer
shove.clearEffects("water")
When multiple effects are added to a layer, they form a processing chain:
In Shöve, global effects are shaders applied to the final composite image after all layers have been rendered and combined. They affect the entire viewport output rather than individual layers. This is implemented using LÖVE's shader system, which processes the pixels of a canvas through a GLSL shader program.
When you apply global effects, here's what happens under the hood:
-- Apply effects to the final composited output
local bloomShader = love.graphics.newShader("bloom.glsl")
-- Persistent: Set up persistent global effects, most common use case
shove.addGlobalEffect(bloomShader)
-- Transient: Apply a transient global effect for a single frame
shove.beginDraw()
-- Draw content
shove.endDraw({bloomShader})
For most use cases requiring consistent effects, addGlobalEffect is the cleaner approach.
For dynamic or temporary effects, passing shaders directly to endDraw provides more flexibility.
addGlobalEffect(bloomShader)This method registers the shader as a persistent global effect. In the implementation:
endDrawremoveGlobalEffect or cleared with clearGlobalEffectsendDrawThis approach is better for:
endDraw({bloomShader})This method applies the shader(s) only for the current frame, when you pass shaders to endDraw:
This approach is useful for:
When multiple effects are added to a layer or set globally, they're applied in sequence:
-- Create a chain of effects
local effects = {
love.graphics.newShader("grayscale.glsl"),
love.graphics.newShader("vignette.glsl"),
love.graphics.newShader("scanlines.glsl")
}
-- Apply the chain to a layer
for _, effect in ipairs(effects) do
shove.addEffect("final", effect)
end
drawOnLayer() provides a convenient way to temporarily switch to a different layer, perform drawing operations, and then automatically return to the previous layer - all without disrupting your main drawing flow.
It elegantly handles all the layer switching mechanics, allowing you to focus on your drawing code rather than layer management.
How it Works:
Example usage:
shove.beginDraw()
-- Draw main content
shove.beginLayer("game")
drawGameWorld()
shove.endLayer()
-- Draw something to a specialized layer with a callback
shove.drawOnLayer("particles", function()
spawnExplosionParticles(x, y)
end)
-- Continue with normal drawing flow
shove.beginLayer("ui")
drawUI()
shove.endLayer()
shove.endDraw()
Here are some good use cases for drawOnLayer():
The drawComposite() function performs an intermediate composite and draw operation during an active drawing cycle.
Specifically, it:
This differs from the typical beginDraw()/endDraw() cycle, where compositing and drawing only happen at the end when endDraw() is called.
The drawComposite() function provides a powerful tool for advanced rendering techniques.
It gives you finer control over the rendering pipeline by allowing intermediate compositing and drawing operations within a single frame.
drawComposite() → Composite and draw the current state with no transient or persistent effectsdrawComposite({anEffect}, false) → Composite and draw the current state with a transient effectdrawComposite({anEffect, anotherEffect}, true) → Composite and draw the current state with transient and persistent effectsdrawComposite(nil, true) → Composite and draw the current state with persistent effectsWhile most games won't need this level of control, it can be useful for complex visual effects, multi-stage rendering, debugging, or interactive applications that need to respond to partially-rendered content. You can manually trigger the compositing process before the end of drawing:
drawComposite()shove.beginDraw()
-- Draw world and characters
shove.beginLayer("world")
drawWorld()
shove.endLayer()
shove.beginLayer("characters")
drawCharacters()
shove.endLayer()
-- Composite and draw what we have so far
shove.drawComposite()
-- Draw second pass with effects that need to see the first pass result
shove.beginLayer("lighting")
drawDynamicLighting() -- This might use rendered result as input
shove.endLayer()
shove.beginLayer("ui")
drawUserInterface()
shove.endLayer()
shove.endDraw()
shove.beginDraw()
-- Draw base layers
-- Show intermediate result for debugging
shove.drawComposite()
-- Debug visualization appears on top
shove.beginLayer("debug")
drawCollisionBoxes()
drawPathfindingGrid()
shove.endLayer()
shove.endDraw()
For cases where layers depend on previous composite results:
shove.beginDraw()
-- Draw background layers
-- Draw to screen so we can capture player input on what's been drawn so far
shove.drawComposite()
-- Get player input based on what they see
local selectedPosition = getPlayerSelection()
-- Continue drawing with new information
shove.beginLayer("selection")
drawSelectionHighlight(selectedPosition)
shove.endLayer()
shove.endDraw()
Manual compositing has some advantages and considerations:
Shöve provides a resize callback system that allows you to register functions that automatically run after window resize events. This is useful for adapting UI layouts, recreating canvases, and handling other resize-dependent operations.
Use shove.setResizeCallback() to register a function to be called after resolution transforms are recalculated during resize operations.
shove.setResizeCallback(function(width, height) -- width and height are the new window dimensions -- Resize-dependent code here end)
shove.getResizeCallback() can be used to retrieve the currently registered resize callback function.
If you need multiple resize handlers, you can implement your own dispatch system:
local resizeHandlers = {}
local function masterResizeCallback()
for _, handler in ipairs(resizeHandlers) do
handler()
end
end
-- Set up the master callback
shove.setResizeCallback(masterResizeCallback)
-- Add handlers as needed
function addResizeHandler(handler)
table.insert(resizeHandlers, handler)
end
Shöve includes a built-in performance profiler that provides real-time information about resolution management, layer status, and rendering performance. This can help diagnose scaling issues and optimize your game during development. The profiler does not have any active code paths active until you enable it, so it has zero impact on performance when hidden.
The profiler overlay displays:
The profiler is rendered after at the end of shove.endDraw() and can be toggled on/off with a keyboard shortcut.
-- The profiler loads automatically when the library initializes function love.draw() shove.beginDraw() -- Your drawing code here shove.endDraw() -- ** profiler rendered at the end of shove.endDraw() here ** end
The Shöve profiler has been highly optimized to have negligible performance impact, even at very high frame rates. This is achieved through several key optimizations:
overlayCanvas), intelligently reallocating only when necessary due to dimension changesshove_collect_metrics event at controlled intervals rather than every frameThe implementation uses an event-driven approach where:
shove_collect_metrics event is pushed periodically during rendering (every collectionInterval * 2 seconds)overlayNeedsUpdate flag only when content has meaningfully changedThis approach ensures metrics are collected at a controlled rate independent of frame rate, while the rendering system efficiently manages when canvas redraws are necessary.
Due to these optimizations, the impact is effectively unmeasurable in typical usage scenarios. The profiler can be left enabled during development without concern for performance degradation.
You can still remove the profiler module entirely as described below.
When running at very high frame rates (many hundred of FPS), the profiler itself introduces a small but measurable performance overhead. In our testing we observed the profiler's impact to be approximately 2% to 4% of total FPS. This overhead comes from the additional calculations, memory access, and UI rendering that the profiler performs to track and display metrics.
For most development scenarios, this minimal impact won't affect your workflow. However, when performing precise performance benchmarking or optimization on high-end systems, consider temporarily disabling the profiler by using the toggle shortcut (Ctrl + P) or removing the profiler module entirely for the most accurate measurements.
The profiler is implemented in a separate file (shove-profiler.lua) so you can easily disable it in production builds.
Remove profiler file in your production builds and Shöve will automatically detect its absence and use a no-op stub implementation.
This approach ensures that the profiler adds zero overhead to your game in production releases while exposing useful tooling during development.
Shöve's layer-based rendering system is designed to work effectively with LÖVE's built-in optimizations:
LÖVE automatically optimizes rendering in several ways:
Shöve complements these optimizations by:
The profiler displays metrics that can help you understand your application's rendering performance, including:
shove.setResolution(width, height, options) - Initialize with game resolutionshove.setWindowMode(width, height, options) - Set window modeshove.updateWindowMode(width, height, options) - Update window modeshove.resize(width, height) - Update when window size changesshove.beginDraw() - Start drawing operationsshove.endDraw(globalEffects) - End drawing and display resultshove.isInViewport(x, y) - Check if coordinates are inside viewportshove.mouseToViewport() - Convert mouse position to viewportshove.screenToViewport(x, y) - Convert screen coordinates to viewportshove.viewportToScreen(x, y) - Convert viewport coordinates to screenshove.getFitMethod() - Get current fit methodshove.setFitMethod(fitMethod) - Set fit methodshove.getRenderMode() - Get current render modeshove.setRenderMode(renderMode) - Set render modeshove.getScalingFilter() - Get current scaling filtershove.setScalingFilter(scalingFilter) - Set scaling filtershove.getResizeCallback() - Get the current resize callbackshove.setResizeCallback(callback) - Register a resize callbackshove.getViewportWidth() - Get viewport widthshove.getViewportHeight() - Get viewport heightshove.getViewportDimensions() - Get viewport dimensionsshove.getViewport() - Get viewport rectangle in screen coordinatesshove.handleDebugKeys() - Display debug informationshove.showDebugInfo(x, y, options) - Display custom debug informationshove.beginLayer(name) - Start drawing to a layershove.endLayer() - Finish drawing to a layershove.createLayer(name, options) - Create a new layershove.removeLayer(name) - Remove a layershove.hasLayer(name) - Check if a layer existsshove.getLayerOrder(name) - Get layer drawing ordershove.setLayerOrder(name, zIndex) - Set layer drawing ordershove.getLayerBlendMode(name) - Get the blend mode and alpha mode of a layershove.setLayerBlendMode(name, blendMode, alphaMode) - Set blend mode and optional alpha modeshove.isLayerVisible(name) - Check if a layer is visibleshove.hideLayer(name) - Hide a layershove.showLayer(name) - Show a layershove.getLayerMask(name) - Get the mask of a layershove.setLayerMask(name, maskName) - Set a layer as a maskshove.drawOnLayer(name, drawFunc) - Draw to a layer with a callbackshove.drawComposite(globalEffects, applyPersistentEffects) - Composite and draw the current stateshove.addEffect(layerName, effect) - Add an effect to a layershove.removeEffect(layerName, effect) - Remove an effect from a layershove.clearEffects(layerName) - Clear all effects from a layershove.addGlobalEffect(effect) - Add a global effectshove.removeGlobalEffect(effect) - Remove a global effectshove.clearGlobalEffects() - Clear all global effectsmore like this
SCYTHE is a lightweight, C#-based game engine focused on modifiability and rapid iteration using Raylib.
Pathfinding library for calculating all node pairs' shortest paths in an unweighted undirected graph.
search projects, people, and tags