Zinggi / elm-2d-game / Game.TwoD.Render

2D rendering module

This module provides a way to render commonly used objects in 2d games like simple sprites and sprite animations.

It also provides colored shapes which can be great during prototyping. The simple shapes can easily be replaced by nicer looking textures later.

suggested import:

import Game.TwoD.Render as Render exposing (Renderable)

Most functions to render something come in 3 forms:

thing, thingZ, thingWithOptions

The first is the most common one where you can specify the size, the position in 2d and some more.

The second one is the same as the first, but with a 3d position. The z position goes from -1 to 1, everything outside this will be invisible. This can be used to put something in front or behind regardless of the render order.

The last one gives you all possible options, e.g. the rotation , the pivot point of the rotation (normalized from 0 to 1), etc.

TODO: insert picture to visualize coordinate system.


type Renderable

A representation of something that can be rendered. To actually render a Renderable onto a web page use the Game.TwoD.* functions


type alias Float2 =
( Basics.Float, Basics.Float )


type alias Float3 =
( Basics.Float
, Basics.Float
, Basics.Float 
)

Basic Shapes

shape : BasicShape -> { o | color : Color, position : Float2, size : Float2 } -> Renderable

A colored shape, great for prototyping


type BasicShape

A representation of a basic shape to use when rendering a ColoredShape

rectangle : BasicShape

BasicShape constructor for a rectangle

triangle : BasicShape

BasicShape constructor for a triangle

circle : BasicShape

BasicShape constructor for a circle

ring : BasicShape

BasicShape constructor for a ring

shapeZ : BasicShape -> { o | color : Color, position : Float3, size : Float2 } -> Renderable

The same, but with 3d position.

shapeWithOptions : BasicShape -> { o | color : Color, position : Float3, size : Float2, rotation : Basics.Float, pivot : Float2 } -> Renderable

A colored shape, that can also be rotated

With texture

Textures are Maybe values because you can never have a texture at the start of your game. You first have to load your textures. In case you pass a Nothing as a value for a texture, A gray rectangle will be displayed instead.

For loading textures I suggest using the game-resources library.

NOTE: Texture dimensions should be a power of 2, e.g. (2^n)x(2^m), like 4x16, 16x16, 512x256, etc. Non power of two texture are possible, but not encouraged.

sprite : { o | texture : Maybe WebGL.Texture.Texture, position : Float2, size : Float2 } -> Renderable

A sprite.

spriteZ : { o | texture : Maybe WebGL.Texture.Texture, position : Float3, size : Float2 } -> Renderable

A sprite with 3d position

spriteWithOptions : { o | texture : Maybe WebGL.Texture.Texture, position : Float3, size : Float2, tiling : Float2, rotation : Basics.Float, pivot : Float2 } -> Renderable

A sprite with tiling and rotation.

spriteWithOptions { config | tiling = ( 3, 5 ) }

will create a sprite with a texture that repeats itself 3 times horizontally and 5 times vertically. TODO: picture!

Animated

animatedSprite : { o | texture : Maybe WebGL.Texture.Texture, position : Float3, size : Float2, bottomLeft : Float2, topRight : Float2, numberOfFrames : Basics.Int, duration : Basics.Float } -> Renderable

An animated sprite. bottomLeft and topRight define a sub area from a texture where the animation frames are located. It's a normalized coordinate from 0 to 1.

TODO: picture!

animatedSpriteWithOptions : { o | texture : Maybe WebGL.Texture.Texture, position : Float3, size : Float2, bottomLeft : Float2, topRight : Float2, rotation : Basics.Float, pivot : Float2, numberOfFrames : Basics.Int, duration : Basics.Float } -> Renderable

the same with rotation

manuallyManagedAnimatedSpriteWithOptions : { o | texture : Maybe WebGL.Texture.Texture, position : Float3, size : Float2, bottomLeft : Float2, topRight : Float2, rotation : Basics.Float, pivot : Float2, numberOfFrames : Basics.Int, currentFrame : Basics.Int } -> Renderable

The same as animated sprite, but you control what frame to display.

Background

parallaxScroll : { o | scrollSpeed : Float2, z : Basics.Float, tileWH : Float2, texture : Maybe WebGL.Texture.Texture } -> Renderable

Used for scrolling backgrounds. This probably wont satisfy all possible needs for a scrolling background, but it can give you something that looks nice quickly.

parallaxScrollWithOptions : { o | scrollSpeed : Float2, z : Basics.Float, tileWH : Float2, offset : Float2, texture : Maybe WebGL.Texture.Texture } -> Renderable

Same but with an offset parameter that you can use to position the background.

Custom

These are useful if you want to write your own GLSL shaders. When writing your own shaders, you might want to look at Game.TwoD.Shaders and Game.TwoD.Shapes for reusable parts.

customFragment : MakeUniformsFunc u -> { b | fragmentShader : WebGL.Shader {} { u | cameraProj : Math.Matrix4.Mat4, transform : Math.Matrix4.Mat4 } { vcoord : Math.Vector2.Vec2 }, pivot : Float2, position : Float3, rotation : Basics.Float, size : Float2 } -> Renderable

This allows you to write your own custom fragment shader. To learn about writing shaders, I recommend this free book.

The type signature may look terrifying, but this is still easier than using veryCustom or using WebGL directly. It handles the vertex shader for you, e.g. your object will appear at the expected location once rendered.

For the fragment shader, you have the vec2 varying vcoord; variable available, which can be used to sample a texture (texture2D(texture, vcoord);). It's a vector that goes from (0,0) to (1,1)

The MakeUniformsFunc allows you to pass along any additional uniforms you may need. In practice, this might look something like this:

makeUniforms { cameraProj, transform, time } =
    { cameraProj = cameraProj, transform = transform, time = time, myUniform = someVector }

render =
    customFragment makeUniforms
        { fragmentShader = frag, position = p, size = s, rotation = 0, pivot = ( 0, 0 ) }

frag =
    [glsl|

precision mediump float;

varying vec2 vcoord;
uniform vec2 myUniform;

void main () {
  gl_FragColor = vcoord.yx + myUniform;
}
|]

Don't pass the time along if your shader doesn't need it.

Here is a small example that draws a circle: https://ellie-app.com/LSTb2NnkDWa1/0


type alias MakeUniformsFunc a =
{ cameraProj : Math.Matrix4.Mat4
, time : Basics.Float
, transform : Math.Matrix4.Mat4 } -> { a | cameraProj : Math.Matrix4.Mat4
, transform : Math.Matrix4.Mat4 
}

Just an alias, needed when you want to use customFragment

veryCustom : ({ camera : Game.TwoD.Camera.Camera, screenSize : ( Basics.Float, Basics.Float ), time : Basics.Float } -> WebGL.Entity) -> Renderable

This allows you to specify your own attributes, vertex shader and fragment shader by using the WebGL library directly. If you use this you have to calculate the transformations yourself. (You can use Shaders.makeTransform)

If you need a square as attributes, you can take the one from Game.TwoD.Shapes

veryCustom
    (\{ camera, screenSize, time } ->
        WebGL.entity myVert
            myFrag
            Shapes.unitSquare
            { u_crazyFrog = frogTexture
            , transform = Shaders.makeTransform ( x, y, z ) 0 ( 2, 4 ) ( 0, 0 )
            , cameraProj = Camera.view camera screenSize
            }
    )

renderTransparent : WebGL.Shader attributes uniforms varyings -> WebGL.Shader {} uniforms varyings -> WebGL.Mesh attributes -> uniforms -> WebGL.Entity

This can be used inside veryCustom instead of WebGL.entity. It's a customized blend function that works well with textures with alpha.

toWebGl : Basics.Float -> Game.TwoD.Camera.Camera -> Float2 -> Renderable -> WebGL.Entity

Converts a Renderable to a WebGL.Entity. You don't need this unless you want to slowly introduce this library in a project that currently uses WebGL directly.

toWebGl time camera ( w, h ) renderable