Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Sprite animation

examples/textures/sprite_anim.livemd

Sprite animation

Mix.install([
  {:zexray, github: "jn-jairo/zexray", depth: 1}
])

Code example

Example complexity rating: [★★☆☆] 2/4

defmodule ExampleState do
  require Record

  Record.defrecord(:example_state,
    scarfy: nil,
    scarfy_info: nil,
    position: nil,
    frame_rec: nil,
    current_frame: 0,
    frames_counter: 0,
    # Number of spritesheet frames shown by second
    frames_speed: 8
  )

  @type example_state ::
          record(:example_state,
            scarfy: Zexray.Type.Texture2D.t_resource(),
            scarfy_info: Zexray.Type.Texture2D.t(),
            position: Zexray.Type.Vector2.t(),
            frame_rec: Zexray.Type.Rectangle.t(),
            current_frame: non_neg_integer,
            frames_counter: non_neg_integer,
            frames_speed: non_neg_integer
          )
end

defmodule Example do
  @resources_dir System.tmp_dir!() <> "/zexray/resources"
  @resources_url "https://github.com/raysan5/raylib/raw/3e336e4470f7975af67f716d4d809441883d7eef"

  use Zexray.Enum
  use Zexray.Type

  import ExampleState

  def download_resources do
    File.mkdir_p!(@resources_dir)

    resources = %{
      "#{@resources_dir}/scarfy.png" => "#{@resources_url}/examples/textures/resources/scarfy.png"
    }

    :inets.start()
    :ssl.start()

    Enum.each(resources, fn {file, url} ->
      if not File.exists?(file) do
        {:ok, :saved_to_file} =
          :httpc.request(:get, {~c"#{url}", []}, [], stream: ~c"#{file}")
      end
    end)
  end

  @screen_width 800
  @screen_height 450
  @title "zexray [textures] example - sprite anim"

  @max_frame_speed 15
  @min_frame_speed 1

  def init do
    # Initialize window
    Zexray.Window.with_window(@screen_width, @screen_height, @title, fn ->
      # Set our game to run at 60 frames-per-second
      Zexray.Timing.set_target_fps(60)

      # Manage resources loading/unloading
      Zexray.Resource.with_resource(
        fn ->
          # Load bunny texture
          scarfy = Zexray.Texture.load("#{@resources_dir}/scarfy.png", :resource)
          scarfy_info = Zexray.Resource.content!(scarfy)
          type_texture_2d(width: scarfy_width, height: scarfy_height) = scarfy_info

          position = type_vector2(x: 350, y: 280)
          frame_rec = type_rectangle(x: 0, y: 0, width: scarfy_width / 6, height: scarfy_height)

          example_state(
            scarfy: scarfy,
            scarfy_info: scarfy_info,
            position: position,
            frame_rec: frame_rec
          )
        end,
        &amp;loop/1
      )
    end)
  end

  defp loop(state) do
    # Detect window close button or ESC key
    if Zexray.Window.should_close?() do
      :ok
    else
      # Update

      example_state(
        scarfy: scarfy,
        scarfy_info: scarfy_info,
        position: position,
        frame_rec: frame_rec,
        current_frame: current_frame,
        frames_counter: frames_counter,
        frames_speed: frames_speed
      ) = state

      type_texture_2d(width: scarfy_width, height: scarfy_height) = scarfy_info

      frames_counter = frames_counter + 1

      {frames_counter, current_frame, frame_rec} =
        if frames_counter >= 60 / frames_speed do
          frames_counter = 0
          current_frame = current_frame + 1

          current_frame = if current_frame > 5, do: 0, else: current_frame

          frame_rec = type_rectangle(frame_rec, x: current_frame * scarfy_width / 6)

          {frames_counter, current_frame, frame_rec}
        else
          {frames_counter, current_frame, frame_rec}
        end

      # Control frames speed

      frames_speed =
        cond do
          Zexray.Keyboard.pressed?(enum_keyboard_key(:right)) -> frames_speed + 1
          Zexray.Keyboard.pressed?(enum_keyboard_key(:left)) -> frames_speed - 1
          true -> frames_speed
        end

      frames_speed = min(@max_frame_speed, max(@min_frame_speed, frames_speed))

      # Draw

      Zexray.Drawing.with_drawing(fn ->
        Zexray.Drawing.clear_background(enum_color(:raywhite))

        type_rectangle(
          x: frame_rec_x,
          y: frame_rec_y,
          width: frame_rec_width,
          height: frame_rec_height
        ) = frame_rec

        Zexray.Texture.draw(scarfy, 15, 40, enum_color(:white))
        Zexray.Shape.draw_rectangle_lines(15, 40, scarfy_width, scarfy_height, enum_color(:lime))

        Zexray.Shape.draw_rectangle_lines(
          15 + frame_rec_x,
          40 + frame_rec_y,
          frame_rec_width,
          frame_rec_height,
          enum_color(:red)
        )

        Zexray.Text.draw("FRAME SPEED:", 165, 210, 10, enum_color(:darkgray))
        Zexray.Text.draw("#{frames_speed} FPS", 575, 210, 10, enum_color(:darkgray))

        Zexray.Text.draw(
          "PRESS RIGHT/LEFT KEYS to CHANGE SPEED!",
          290,
          240,
          10,
          enum_color(:darkgray)
        )

        0..(@max_frame_speed - 1)
        |> Enum.each(fn i ->
          if i < frames_speed do
            Zexray.Shape.draw_rectangle(250 + 21 * i, 205, 20, 20, enum_color(:red))
          else
            Zexray.Shape.draw_rectangle_lines(250 + 21 * i, 205, 20, 20, enum_color(:maroon))
          end
        end)

        # Draw part of the texture
        Zexray.Texture.draw_rec(scarfy, frame_rec, position, enum_color(:white))

        Zexray.Text.draw(
          "(c) Scarfy sprite by Eiden Marsal",
          @screen_width - 200,
          @screen_height - 20,
          10,
          enum_color(:gray)
        )
      end)

      state
      |> example_state(
        frame_rec: frame_rec,
        current_frame: current_frame,
        frames_counter: frames_counter,
        frames_speed: frames_speed
      )
      |> loop()
    end
  end
end
Example.download_resources()
Example.init()