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

Bunnymark

examples/textures/bunnymark.livemd

Bunnymark

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

Code example

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

defmodule Bunny do
  require Record
  require Zexray.Enum.Color
  require Zexray.Type.Vector2

  Record.defrecord(:bunny,
    position: Zexray.Type.Vector2.t(x: 0, y: 0),
    speed: Zexray.Type.Vector2.t(x: 0, y: 0),
    color: Zexray.Enum.Color.enum(:black)
  )

  @type bunny ::
          record(:bunny,
            position: Zexray.Type.Vector2.t(),
            speed: Zexray.Type.Vector2.t(),
            color: Zexray.Type.Color.t()
          )
end

defmodule ExampleState do
  require Record

  Record.defrecord(:example_state,
    tex_bunny: nil,
    tex_bunny_info: nil,
    bunnies_count: 0,
    bunnies: []
  )

  @type example_state ::
          record(:example_state,
            tex_bunny: Zexray.Type.Texture2D.t_resource(),
            tex_bunny_info: Zexray.Type.Texture2D.t(),
            bunnies_count: non_neg_integer,
            bunnies: [Bunny.bunny()]
          )
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 Zexray.Util, only: [random_number: 2]

  import Bunny
  import ExampleState

  def download_resources do
    File.mkdir_p!(@resources_dir)

    resources = %{
      "#{@resources_dir}/wabbit_alpha.png" =>
        "#{@resources_url}/examples/textures/resources/wabbit_alpha.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 - bunnymark"

  # bunnies limit
  @max_bunnies 500

  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
          tex_bunny = Zexray.Texture.load("#{@resources_dir}/wabbit_alpha.png", :resource)
          tex_bunny_info = Zexray.Resource.content!(tex_bunny)

          example_state(
            tex_bunny: tex_bunny,
            tex_bunny_info: tex_bunny_info
          )
        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(
        tex_bunny: tex_bunny,
        tex_bunny_info: tex_bunny_info,
        bunnies_count: bunnies_count,
        bunnies: bunnies
      ) = state

      type_texture_2d(width: tex_bunny_width, height: tex_bunny_height) = tex_bunny_info

      {bunnies_count, bunnies} =
        if Zexray.Mouse.down?(enum_mouse_button(:left)) do
          position = Zexray.Mouse.get_position()

          # Create more bunnies
          1..10
          |> Enum.reduce({bunnies_count, bunnies}, fn _, {bunnies_count, bunnies} ->
            if bunnies_count < @max_bunnies do
              bunny =
                bunny(
                  position: position,
                  speed:
                    type_vector2(
                      x: random_number(-250, 250) / 60,
                      y: random_number(-250, 250) / 60
                    ),
                  color:
                    type_color(
                      r: random_number(50, 240),
                      g: random_number(80, 240),
                      b: random_number(100, 240),
                      a: 255
                    )
                )

              {bunnies_count + 1, [bunny | bunnies]}
            else
              {bunnies_count, bunnies}
            end
          end)
        else
          {bunnies_count, bunnies}
        end

      # Update bunnies
      bunnies =
        bunnies
        |> Enum.map(fn bunny ->
          bunny(position: position, speed: speed) = bunny
          type_vector2(x: position_x, y: position_y) = position
          type_vector2(x: speed_x, y: speed_y) = speed

          position_x = position_x + speed_x
          position_y = position_y + speed_y

          speed_x =
            if position_x + tex_bunny_width / 2 > @screen_width or
                 position_x + tex_bunny_width / 2 < 0 do
              speed_x * -1
            else
              speed_x
            end

          speed_y =
            if position_y + tex_bunny_height / 2 > @screen_height or
                 position_y + tex_bunny_height / 2 - 40 < 0 do
              speed_y * -1
            else
              speed_y
            end

          bunny(
            bunny,
            position: type_vector2(x: position_x, y: position_y),
            speed: type_vector2(x: speed_x, y: speed_y)
          )
        end)

      # Draw

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

        Enum.each(bunnies, fn bunny ->
          bunny(position: position, color: color) = bunny
          type_vector2(x: position_x, y: position_y) = position
          Zexray.Texture.draw(tex_bunny, position_x, position_y, color)
        end)

        Zexray.Shape.draw_rectangle(0, 0, @screen_width, 40, enum_color(:black))
        Zexray.Text.draw("bunnies: #{bunnies_count}", 120, 10, 20, enum_color(:green))

        Zexray.Text.draw_fps(10, 10)
      end)

      state
      |> example_state(
        bunnies_count: bunnies_count,
        bunnies: bunnies
      )
      |> loop()
    end
  end
end
Example.download_resources()
Example.init()