GithubHelp home page GithubHelp logo

17cupsofcoffee / tetra Goto Github PK

View Code? Open in Web Editor NEW
887.0 18.0 61.0 9.76 MB

๐ŸŽฎ A simple 2D game framework written in Rust

License: MIT License

Rust 99.90% GLSL 0.10%
rust gamedev game-frameworks game-development 2d-game-framework game-engine

tetra's Introduction

Tetra

Build Status Crates.io Documentation License

Tetra is a simple 2D game framework written in Rust. It uses SDL2 for event handling and OpenGL 3.2+ for rendering.

Status

Tetra is being passively maintained, as of January 2022.

No new features are planned, but occasional bugfix updates may still be released from time to time. PRs may be accepted if they don't have a large maintainence burden.

If you're looking for a similar framework that's more actively developed, try macroquad or GGEZ. Alternatively, you can try nova, my spiritual successor to this library (much smaller scope, still very experimental).

Features

  • XNA/MonoGame-inspired API
  • Efficient 2D rendering, with draw call batching by default
  • Easy input handling, via polling or events, with support for gamepads
  • Deterministic game loop by default, ร  la Fix Your Timestep
  • Common building blocks built-in, such as:
    • Font rendering
    • Cameras
    • Screen scaling

Installation

To add Tetra to your project, add the following line to your Cargo.toml file:

tetra = "0.8"

You will also need to install the SDL2 native libraries - full details are provided in the documentation.

Examples

To get a simple window displayed on screen, the following code can be used:

use tetra::graphics::{self, Color};
use tetra::{Context, ContextBuilder, State};

struct GameState;

impl State for GameState {
    fn draw(&mut self, ctx: &mut Context) -> tetra::Result {
        // Cornflower blue, as is tradition
        graphics::clear(ctx, Color::rgb(0.392, 0.584, 0.929));
        Ok(())
    }
}

fn main() -> tetra::Result {
    ContextBuilder::new("Hello, world!", 1280, 720)
        .build()?
        .run(|_| Ok(GameState))
}

You can see this example in action by running cargo run --example hello_world.

The full list of examples is available here.

Support/Feedback

Tetra is fairly early in development, so you might run into bugs/flaky docs/general weirdness. Please feel free to open an issue/PR if you find something! You can also contact me via Twitter or the Rust Game Development Discord.

tetra's People

Contributors

17cupsofcoffee avatar aaneto avatar dependabot-preview[bot] avatar dependabot[bot] avatar fililip avatar ggalizzi avatar johanlindfors avatar liquidityc avatar puppetmaster- avatar rghartmann avatar selimeren avatar sumibi-yakitori avatar tairesh avatar tatref avatar teknico avatar tesselode avatar timerertim avatar victorkoenders avatar vrmiguel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tetra's Issues

Investigate which events get fired on startup

While debugging a different issue, I noticed that on startup, we process quite a few events, a lot of which are a result of window setup. I need to make sure none of them are causing redundant GL calls or slowing down startup.

Make error handling more robust

At the minute, errors are divided up into their source crates - i.e. there's an error for GL, an error for SDL, and so on. This is nice and simple, but makes life harder for people who are actually using the library - there's no way to tell what kind of error it is without looking at strings in a lot of cases.

I think long term, we need to replace every instance of TetraError::Sdl and TetraError::OpenGl with a more specific variant that actually describes what happened. We can still provide the original string as the source if we need to.

Screen scaling is not aligned to pixels

When scaling the screen, sometimes floating point error leads to the screen not quite being an integer width and height, which causes some weird texture artifacts (wrapping, etc.). We should probably just truncate the numbers, or do all of the math with integers.

Scaling doesn't take into account the origin

The origin is applied after scaling, rather than before - this feels a bit weird? It means if you set the origin to the center and then scale, the image will no longer be centered. We probably want to change this.

Two sources of truth for active textures/shaders

It occurred to me last night that we track the active texture and shader in two places:

  • The opengl layer, for the purpose of preventing needless state changes.
  • The graphics layer, for the purpose of auto-flushing batched geometry.

It'd probably make things simpler if we could unify these somehow? I've got visions of them getting out of sync and making things horrendous to debug...

Develop some example projects

We need some code samples.

  • Hello world (the absolute minimum code to get something running)
  • Rendering an image
  • Keyboard input
  • Mouse input
  • A simple game (Tetris?)

Also, I should probably start adding examples as I write new features - that way I can test API changes.

Unclear what units are used by the tick rate methods

The docs for the tick rate methods say that they take values in ticks per second, but they actually take values specifying the length of the tick (i.e. 1.0 / ticks_per_second).

We either need to update the docs to reflect this, or change the behavior to match the docs.

Add rotation to DrawParams

This isn't quite as simple as it might appear at first glance - in my graphics programming naivety, I didn't take into account rotation when writing the logic for rendering quads, so applying the matrix only properly rotates two vertices. Fixable (I have something working on a local branch), but a bit of a pain.

See if we can generate the index array data using const fn

Once we upgrade to Rust 1.31, we might be able to generate the static data used to fill the index array at compile time instead of at runtime - it's probably not a huge performance gain, but if it'd be easy it'd be nice to do it!

Audio thread crashes when playback ends on a detached instance of an MP3

Reported by @puppetmaster- (as are a lot of these bugs :p) and replicated on my work laptop - but not my main machine, which is strange...

The issue seems to get triggered under the following circumstances:

  • The Sound has to have been created from an MP3.
  • The SoundInstance has to have been dropped.
  • Playback has just finished.
  • EDIT: The sample rate of the sound doesn't match that of the output/sound card.
  • EDIT: The project was built in debug mode (it's a debug only assertation that's triggering).

Custom shaders

Users should be able to set the shader that's being used for drawing.

The only tricky bit here is figuring out how this interacts with the framebuffer - it might be quite surprising to set a shader and have it also impact the entire screen. Maybe separate slots for shaders and 'post-processing' shaders?

Creating the DrawParams matrix every frame is *slow*

Especially in debug mode, this seems to be by far the biggest bottleneck. I tried replacing that code with something more like what LibGDX uses (i.e. basic trig instead of matrix math) and suddenly our BunnyMark performance jumped up to being close to Amethyst's, so that might be a good route to go.

Standardize constructor naming

Most of the types take a path in their new methods, but Shader takes strings and has a seperate from_file method.

We should standardize on a naming scheme for constructors - the two options that are appealing to me right now are:

  • new is always the one that takes in a file, as that's usually what you want. from_something functions for everything else.
  • Don't have a new at all, and make everything a from_something. This is a little extra typing, but I kinda like the clarity, and it means one constructor isn't given higher importance than the others.

Black screen/shader issues on MacOS

Running on osX mojave. Macbook pro retina. Download from GIT and run

cargo run --example hello_world

Compiling tetra v0.2.1 (/Users/pabloweremczuk/Documents/Proyectos/rust/tetra)
Finished dev [unoptimized + debuginfo] target(s) in 6.41s
Running target/debug/examples/texture
OpenGL Device: AMD Radeon R9 M370X OpenGL Engine
OpenGL Driver: 2.1 ATI-2.4.9
OpenGL Vendor: ATI Technologies Inc.
Swap Interval: VSync
Error: OpenGl("ERROR: 0:1: '' : version '130' is not supported\n")

Add other screen scaling modes

We currently always scale the screen in such a way that the aspect ratio is maintained, adding letterboxing if required. This might not always be what people want!

Add extra options to the ContextBuilder

Off the top of my head, it'd be nice to be able to start the window:

  • Maximized
  • Minimized
  • Fullscreen
  • With the mouse pointer visible (currently the default, but it probably shouldn't be...)
  • Borderless

We also really ought to allow changing all of these settings (and the others) at runtime too.

Set up a proper release process

I think it would be handy to have a separate branch for development, if only so that the README/docs site don't show functionality that's completely unimplemented on the current version...

Consider allowing different timestep modes

While I'm fully on the Fix Your Timestep train, I know this might not be right for every game/every developer, so it'd probably be worth investigating how we could allow the user to choose between fixed and variable length updates.

OpenGL handles can use-after-free if context is dropped

The Drop implementations for the OpenGL handle types (GLTexture, GLProgram, etc.) unconditionally try to delete their associated GL objects - this might get dodgy if the context gets dropped.

Probably not likely to be a problem in practice, but worth keeping in mind!

Upgrade to Rust 2018

Need to have a look at the backwards compatibility implications of this, but once Rust 1.31 gets released today, we should convert to the 2018 idioms.

Consider re-exporting Vec2

In all of the examples, the only thing imported from tetra::glm is Vec2 - we could maybe save people from having to add an import if we re-(re-)exported it in one of the main modules.

Implement nine-slice textures

Adding a full UI system is probably a way off (and I don't know whether it's even something that should go in this crate), but a lot of use cases could probably be solved by adding an easy way of doing nine-slice scaling.

Add bitmap fonts

I previously decided against this in #17, since I thought TTF rendering would suffice, but I'm less sure now - TTFs don't work as well when the screen is scaled from a very small size, which is one of the main ways I'm using Tetra.

Add animation examples

We don't currently have any examples of the animation functionality. We should either add a new one, or integrate it into one of the others.

Look at alternative ways of implementing the graphics backend

Currently I'm using the gl crate and writing the unsafe wrapper myself, which scares me a bit...

Long term, we might want to move to something like gfx-hal, but we might be able clean things up a bit short-term by using the gleam wrapper library that Servo uses. It's basically just the code I've been having to write anyway, but much better tested and written by people who know what they're doing ๐Ÿ˜…

EDIT: Okay I'm going to use this as a general purpose tracking issue for 'what do we use for getting pixels on the screen':

  • gleam - Would probably be the simplest change, as it's basically as thin a GL wrapper as you can possibly write. This would probably be a nice short term step,
  • gfx-hal - I like the idea of using this (and if I do, it'll probably be via contributing to whatever Icefoxen is working on) - that said, it doesn't currently look simple to get it working with SDL, and I'm categorically not dropping SDL until the Rust alternatives are more developed.
  • glium - Probably the nicest/easiest to use GL wrapper, although I have some concerns about bugs/maintainance based on discussions I've seen on Discord.
  • luminance - Looks quite 'Glium`-ish, might also be worth considering (especially as it seems a little less opinionated).

Audio playback

It seems like there's effectively three games in town for audio playback in Rust:

  • Rodio, which is written in pure Rust, but has a bit of a quirky API.
  • OpenAL Soft, which is extremely powerful but would require another C dependency (which can't be static linked due to the LGPL).
  • SDL, which we already depend on and is stable but is extremely bare bones (i.e. we'd have to write our own mixer).

Of the above, only Rodio has built-in file decoders, so I'm leaning that way at the minute.

Port BunnyMark to Tetra

This would be really helpful, both for the purposes of testing performance (i.e. has the change I've made completely destroyed the framerate for non-trivial applications), and for demonstrating the difference between opt-level settings.

The renderer should flush instead of panicking if it hits capacity

While trying to port rl to Tetra, I ran into the issue that the screen was made up of too many quads to fit into the sprite limit - I was able to get around this by manually flushing, but it seems like it'd be a better idea to just flush automatically if we run out of space.

Also, we should probably make sure the renderer's default capacity makes sense, as there's not currently a way to override it.

Also, do we even need to have a capacity? Could we do something fun with instanced rendering for simple quad drawing?

Re-export `tetra::error::Result` from the crate root

It's one less line of imports, and it'll allow people to do tetra::Result, which is more idiomatic than importing Result directly and clobbering the std version.

Speaking of which, we should update the examples/docs to reflect that.

Black screen when using Mesa drivers (?)

After installing the -lSDL2 dependency:

apt-get install libsdl2-dev

I can compile and run the examples (on Debian), but they all render black without error messages:

$ uname -a
Linux purism 4.15.0-2-amd64 #1 SMP Debian 4.15.11-1 (2018-03-20) x86_64 GNU/Linux
$ cargo run --example hello_world
    Finished dev [unoptimized + debuginfo] target(s) in 0.05s
     Running `target/debug/examples/hello_world`
OpenGL Device: Mesa DRI Intel(R) Iris 6100 (Broadwell GT3)
OpenGL Driver: 3.0 Mesa 18.1.9
OpenGL Vendor: Intel Open Source Technology Center
Swap Interval: VSync

Change return types for functions/constructors that might fail in the future

Some functions (e.g. Shader::from_string) don't currently return Result, as they don't perform proper error handling. We should pre-emptively wrap the return types in Result so that we don't have to make another breaking change later on.

This is less of a concern for functions that currently return (), as adding a Result return will just give the user a warning.

Fix Your Timestep! link in README *on crates.io* is broken

Thanks for taking the time to create tetra. I saw your tweet on publishing it to crates.io and wanted to check it out.

Since you also wanted to get a heads up on any bugs I am creating this issue. Due note that it seems to me to be a issue with crates.io but I am not sure. I would like to investigate further, but in the meantime I would like to refer back to the first context I found it.

When I open tetra on crates.io and click on the Fix Your Timestep! link, I am redirected to a GitHub 404 page.

The reason is that the url is

https://github.com/17cupsofcoffee/tetra/blob/master/*https://gafferongames.com/post/fix_your_timestep/

When I go to the tetra repository and click the same Fix Your Timestep! I end up at the correct article.

Like I said, this is in no part your problem. I will try to investigate what causes this, and then maybe transfer the link to crates.io

Implement text rendering

At the very least, we should support bitmap fonts (probably through the BMFont format), but it'd be nice to also get TTFs working directly via glyph-brush.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.