From: Alexis Laferrière Date: Sat, 12 Dec 2015 00:19:06 +0000 (-0500) Subject: lib/gamnit: intro the asteronit example X-Git-Tag: v0.8~36^2~8 X-Git-Url: http://nitlanguage.org lib/gamnit: intro the asteronit example Signed-off-by: Alexis Laferrière --- diff --git a/lib/gamnit/examples/asteronits/Makefile b/lib/gamnit/examples/asteronits/Makefile new file mode 100644 index 0000000..dae31dd --- /dev/null +++ b/lib/gamnit/examples/asteronits/Makefile @@ -0,0 +1,34 @@ +NITC=../../../../bin/nitc +NITLS=../../../../bin/nitls + +all: bin/asteronits + +bin/asteronits: $(shell ${NITLS} -M src/asteronits.nit linux) ${NITC} pre-build + ${NITC} src/asteronits.nit -m linux -o $@ + +bin/texture_atlas_parser: src/texture_atlas_parser.nit + ${NITC} src/texture_atlas_parser.nit -o $@ + +src/controls.nit: art/controls.svg + ../../../../contrib/inkscape_tools/bin/svg_to_png_and_nit art/controls.svg -a assets/ -s src/ -x 2.0 -g + +src/spritesheet_city.nit: bin/texture_atlas_parser + bin/texture_atlas_parser art/sheet.xml --dir src/ -n spritesheet + +pre-build: src/controls.nit src/spritesheet_city.nit + +check: bin/asteronits + bin/asteronits + +# --- +# Android + +android: bin/asteronits.apk +bin/asteronits.apk: $(shell ${NITLS} -M src/asteronits.nit android) ${NITC} res/drawable-hdpi/icon.png + ${NITC} src/touch_ui.nit -m android -o $@ + +android-release: $(shell ${NITLS} -M src/asteronits.nit android) ${NITC} res/drawable-hdpi/icon.png + ${NITC} src/touch_ui.nit -m android -o bin/asteronits.apk --release + +res/drawable-hdpi/icon.png: art/icon.svg + ../../../../contrib/inkscape_tools/bin/svg_to_icons --out res --android art/icon.svg diff --git a/lib/gamnit/examples/asteronits/README.md b/lib/gamnit/examples/asteronits/README.md new file mode 100644 index 0000000..aefee88 --- /dev/null +++ b/lib/gamnit/examples/asteronits/README.md @@ -0,0 +1,13 @@ +Sample portable 2D game implemented with the `simple_2d` API of gamnit + +This projects is organized in 3 modules, one per concern: + +* `game_logic` defines the pure game logic without any of the display details. + +* `asteronits` implements the display logic with support for keyboard input events. + +* `touch_ui` adds an interface for touchscreen devices with buttons to open fire and control the ship. + +# Art + +Artwork created by Kenney.nl under CC0 diff --git a/lib/gamnit/examples/asteronits/art/controls.svg b/lib/gamnit/examples/asteronits/art/controls.svg new file mode 100644 index 0000000..903898d --- /dev/null +++ b/lib/gamnit/examples/asteronits/art/controls.svg @@ -0,0 +1,2198 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/gamnit/examples/asteronits/art/icon.svg b/lib/gamnit/examples/asteronits/art/icon.svg new file mode 100644 index 0000000..d8b9998 --- /dev/null +++ b/lib/gamnit/examples/asteronits/art/icon.svg @@ -0,0 +1,1886 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/gamnit/examples/asteronits/art/sheet.xml b/lib/gamnit/examples/asteronits/art/sheet.xml new file mode 100644 index 0000000..71e1ccf --- /dev/null +++ b/lib/gamnit/examples/asteronits/art/sheet.xml @@ -0,0 +1,296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/gamnit/examples/asteronits/assets/.gitignore b/lib/gamnit/examples/asteronits/assets/.gitignore new file mode 100644 index 0000000..23a22a6 --- /dev/null +++ b/lib/gamnit/examples/asteronits/assets/.gitignore @@ -0,0 +1 @@ +images/controls.png diff --git a/lib/gamnit/examples/asteronits/assets/images/sheet.png b/lib/gamnit/examples/asteronits/assets/images/sheet.png new file mode 100644 index 0000000..8c58b86 Binary files /dev/null and b/lib/gamnit/examples/asteronits/assets/images/sheet.png differ diff --git a/lib/gamnit/examples/asteronits/bin/.gitignore b/lib/gamnit/examples/asteronits/bin/.gitignore new file mode 100644 index 0000000..72e8ffc --- /dev/null +++ b/lib/gamnit/examples/asteronits/bin/.gitignore @@ -0,0 +1 @@ +* diff --git a/lib/gamnit/examples/asteronits/org.nitlanguage.asteronits.txt b/lib/gamnit/examples/asteronits/org.nitlanguage.asteronits.txt new file mode 100644 index 0000000..9f47066 --- /dev/null +++ b/lib/gamnit/examples/asteronits/org.nitlanguage.asteronits.txt @@ -0,0 +1,10 @@ +Categories:Nit,Games +License:Apache2 +Web Site:http://nitlanguage.org +Source Code:http://nitlanguage.org/nit.git/tree/HEAD:/lib/gamnit/examples/asteronits +Issue Tracker:https://github.com/nitlang/nit/issues + +Summary:Destroy asteroids in this simple 2D sample game made with gamnit +Description: +Sample portable gamnit game implemented with the simple_2d API. +. diff --git a/lib/gamnit/examples/asteronits/package.ini b/lib/gamnit/examples/asteronits/package.ini new file mode 100644 index 0000000..ebdbce2 --- /dev/null +++ b/lib/gamnit/examples/asteronits/package.ini @@ -0,0 +1,12 @@ +[package] +name=asteronits +tags=example +maintainer=Alexis Laferrière +license=Apache-2.0 +[upstream] +browse=https://github.com/nitlang/nit/tree/master/lib/gamnit/examples/asteronits/ +git=https://github.com/nitlang/nit.git +git.directory=lib/gamnit/examples/asteronits/ +homepage=http://nitlanguage.org +issues=https://github.com/nitlang/nit/issues +apk=http://nitlanguage.org/fdroid/apk/asteronits.apk diff --git a/lib/gamnit/examples/asteronits/res/.gitignore b/lib/gamnit/examples/asteronits/res/.gitignore new file mode 100644 index 0000000..72e8ffc --- /dev/null +++ b/lib/gamnit/examples/asteronits/res/.gitignore @@ -0,0 +1 @@ +* diff --git a/lib/gamnit/examples/asteronits/src/.gitignore b/lib/gamnit/examples/asteronits/src/.gitignore new file mode 100644 index 0000000..2e17176 --- /dev/null +++ b/lib/gamnit/examples/asteronits/src/.gitignore @@ -0,0 +1,2 @@ +controls.nit +spritesheet.nit diff --git a/lib/gamnit/examples/asteronits/src/asteronits.nit b/lib/gamnit/examples/asteronits/src/asteronits.nit new file mode 100644 index 0000000..588bdb6 --- /dev/null +++ b/lib/gamnit/examples/asteronits/src/asteronits.nit @@ -0,0 +1,213 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Portable game to destroy asteroids +module asteronits is + app_name "Asteronits" + app_namespace "org.nitlanguage.asteronits" + app_version(1, 0, git_revision) + + android_manifest_activity """android:screenOrientation="sensorLandscape"""" + android_api_target 15 +end + +import gamnit::simple_2d + +import game_logic +import spritesheet + +redef class Spritesheet + # Largest meteors, organized by color + var meteors_big: Array[Array[Texture]] = [ + [meteor_brown_big1, meteor_brown_big2, meteor_brown_big3, meteor_brown_big4], + [meteor_grey_big1, meteor_grey_big2, meteor_grey_big3, meteor_grey_big4]] + + # Medium size meteors, organized by color + var meteors_med: Array[Array[Texture]] = [ + [meteor_brown_med1, meteor_brown_med3], + [meteor_grey_med1, meteor_grey_med2]] + + # Small meteors, organized by color + var meteors_small: Array[Array[Texture]] = [ + [meteor_brown_small1, meteor_brown_small2], + [meteor_grey_small1, meteor_grey_small2]] + + # Tiny meteors, organized by color + # + # TODO use these in particles + var meteors_tiny: Array[Array[Texture]] = [ + [meteor_brown_tiny1, meteor_brown_tiny2], + [meteor_grey_tiny1, meteor_grey_tiny2]] + + # Available ships + var ships: Array[Texture] = [enemy_green1] +end + +redef class App + + # Current world in play + var world = new World(12, 2, display.aspect_ratio) is lazy + + redef fun on_create + do + super + + # Move the camera to show all the world world in the screen range + world_camera.reset_height(world.half_height * 2.0) + end + + # Main spritesheet with ships, asteroids and beams + var spritesheet = new Spritesheet + + redef fun update(dt) + do + # Update game logic + world.do_turn dt + + # Setup new world if all asteroids are destroyed + if world.asteroids.is_empty then + sprites.clear + world = new World(world.n_asteroids*2, world.n_asteroid_parts+1, display.aspect_ratio) + end + end + + redef fun accept_event(event) + do + if event isa QuitEvent then + exit 0 + else if event isa KeyEvent then + var thrust = event.thrust + if thrust != 0.0 then + app.world.ship.applied_thrust = if event.is_down then thrust else 0.0 + return true + end + + var rot = event.rotation + if rot != 0.0 then + app.world.ship.applied_rotation = if event.is_down then rot else 0.0 + return true + end + + if event.name == "space" and event.is_down then + app.world.ship.fire + return true + else if event.name == "escape" then + exit 0 + end + end + + return false + end +end + +redef class SpacialObject + # Main `Sprite` to draw for this object + var sprite: Sprite is noinit + + # All `Sprites` composing this object + var sprites: Collection[Sprite] = new Ref[Sprite](sprite) is lazy + + init do app.sprites.add_all sprites + + redef fun do_turn(dt) + do + super + sprite.rotation = rotation + pi/2.0 + end + + redef fun destroy + do + super + for s in sprites do app.sprites.remove s + end +end + +redef class Asteroid + + init + do + # Select texture from `size` and `color` + var tex = if size == 3 then + app.spritesheet.meteors_big[color].rand + else if size == 2 then + app.spritesheet.meteors_med[color].rand + else app.spritesheet.meteors_small[color].rand + + sprite = new Sprite(tex, center) + super + end +end + +redef class Bullet + init + do + sprite = new Sprite(app.spritesheet.laser_blue01, center) + super + end +end + +redef class Ship + init + do + sprite = new Sprite(app.spritesheet.ships.rand, center) + sprites = [sprite, thrust_sprite] + + super + end + + private var thrust_sprite = new Sprite(app.spritesheet.fire09, new Point3d[Float](0.0, 0.0, 0.0)) + + private var sprites_with_fire: Array[Sprite] = [thrust_sprite, sprite] is lazy + + redef fun do_turn(dt) + do + super + + # Update position of the thrust sprite + var dist_to_engine = 45.0 + thrust_sprite.center.x = center.x - dist_to_engine*rotation.cos + thrust_sprite.center.y = center.y - dist_to_engine*rotation.sin + thrust_sprite.center.z = center.z + thrust_sprite.rotation = rotation + pi/2.0 + + # Show or hide the thrust sprite + if applied_thrust > 0.0 then + thrust_sprite.alpha = 1.0 + else if thrust_sprite.alpha > 0.0 then + thrust_sprite.alpha -= dt*4.0 + if thrust_sprite.alpha < 0.0 then thrust_sprite.alpha = 0.0 + end + + # HACK, the "enemy" ship used for the player points downwards + sprite.rotation += pi + end +end + +redef class KeyEvent + + # How does this event affect the ship thrust? + fun thrust: Float + do + if is_arrow_up or name == "w" then return 1.0 + return 0.0 + end + + # How does this event affect the ship thrust? + fun rotation: Float + do + if is_arrow_right or name == "d" then return 1.0 + if is_arrow_left or name == "a" then return -1.0 + return 0.0 + end +end diff --git a/lib/gamnit/examples/asteronits/src/game_logic.nit b/lib/gamnit/examples/asteronits/src/game_logic.nit new file mode 100644 index 0000000..8d45e54 --- /dev/null +++ b/lib/gamnit/examples/asteronits/src/game_logic.nit @@ -0,0 +1,272 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Pure game logic, independent of gamnit and other display concerns +module game_logic + +import geometry::points_and_lines + +# Root of all entities of a single play +class World + + # Number of original asteroids per play + var n_asteroids: Int + + # Number of parts created when an asteroid explodes + var n_asteroid_parts: Int + + # Ratio of the world: height / width + var ratio_height_width: Float + + # Minimum half size of the world, applied either to `half_width` of `half_height` + private var min_half_size = 500.0 + + # Width of the world + var half_width: Float = if ratio_height_width <= 1.0 then + min_half_size + else min_half_size * ratio_height_width is lazy + + # Height of the world + var half_height: Float = if ratio_height_width >= 1.0 then + min_half_size + else min_half_size / ratio_height_width is lazy + + # Player's ship + var ship = new Ship(self) + + # All live spacial objects + var objects = new Array[SpacialObject].with_items(ship) + + # All live asteroids + var asteroids = new Array[Asteroid] + + # All live bullets + var bullets = new Array[SpacialObject] + + init + do + for a in n_asteroids.times do + var asteroid = new Asteroid(self, 3) + asteroid.center.x = half_width - 2.0*half_width.rand + asteroid.center.y = half_height - 2.0*half_height.rand + asteroid.rotation_inertia = 0.5 - 1.0.rand + + objects.add asteroid + asteroids.add asteroid + end + end + + # Execute a turn that took `dt` seconds + fun do_turn(dt: Float) + do + for object in objects do object.do_turn dt + + for object in objects.to_a do if not object isa Asteroid then + + for asteroid in asteroids.to_a do + var d2 = object.center.dist2(asteroid.center) + + var r = object.radius + asteroid.radius + if d2 < r*r then + # Boom + if object == ship then + # The ship is invincible + # TODO health and losing + else + object.destroy + end + + asteroid.destroy + break + end + end + end + end +end + +# Physical object in space physics +abstract class SpacialObject + + # World in which this object belongs + var world: World + + # Current position + var center = new Point3d[Float](0.0, 0.0, 0.0) + + # Position inertia, applied on `center` at each `do_turn` + var inertia = new Point3d[Float](0.0, 0.0, 0.0) + + # Current rotation + var rotation = 0.0 + + # Rotation inertia, applied on `rotation` at each `do_turn` + var rotation_inertia = 0.0 + + # Rotation force, currently applied by the pilot + var applied_rotation = 0.0 is writable + + # Thrust force, currently applied by the pilot + var applied_thrust = 0.0 is writable + + # Radius of this object for collision detection + var radius: Float is noinit + + # New instance copying the data from `other` with an optional `variation` + init copy(other: SpacialObject, variation: nullable Float) + do + init other.world + + if variation == null then variation = 0.0 + + center.x = other.center.x + center.y = other.center.y + center.z = other.center.z + + inertia.x = other.inertia.x + variation - 2.0*variation.rand + inertia.y = other.inertia.y + variation - 2.0*variation.rand + + rotation = other.rotation + if variation != 0.0 then rotation = 2.0 * pi.rand + end + + # Apply `thrust` forward on this object + fun apply_thrust(thrust: Float) + do + inertia.x += thrust * rotation.cos + inertia.y += thrust * rotation.sin + end + + # Execute a turn that took `dt` seconds + fun do_turn(dt: Float) + do + # Forces to inertia + var t = applied_thrust * 5.0 + inertia.x += t * rotation.cos + inertia.y += t * rotation.sin + + # Arcade rotation + var r = applied_rotation * 0.05 + rotation += r + + # Realistic rotation, kept for reference and reality minded individuals + #var r = applied_rotation * 0.2 + #rotation_inertia += r + #rotation_inertia = rotation_inertia.min(2.0).max(-2.0) + + # Inertia to position + rotation += rotation_inertia * dt + center.x += inertia.x * dt + center.y += inertia.y * dt + center.z += inertia.z * dt + + # Wrap objects so they stay in the screen + while center.x < -world.half_width do center.x += 2.0 * world.half_width + while center.x > world.half_width do center.x -= 2.0 * world.half_width + while center.y < -world.half_height do center.y += 2.0 * world.half_height + while center.y > world.half_height do center.y -= 2.0 * world.half_height + end + + # Destroy this object + fun destroy do world.objects.remove self +end + +# Player's ship +class Ship + super SpacialObject + + init do radius = 20.0 + + # Open fire forward + fun fire + do + var bullet = new Bullet.copy(world.ship) + bullet.center.z = -1.0 # in the background + bullet.apply_thrust 500.0 # give a boost + + world.objects.add bullet + world.bullets.add bullet + end +end + +# Asteroid, the main obstacle in this game +class Asteroid + super SpacialObject + + # Size of this asteroid, should be greater than 0 + var size: Int + + # Color, or type, on this asteroid + var color: Int = 2.rand + + init + do + rotation_inertia = 0.5 - 1.0.rand + radius = 22.5 * size.to_f + end + + # New asteroid breaking off from `other` + init break_off(other: Asteroid) + do + size = other.size - 1 + color = other.color + + copy(other, 60.0) + end + + # Explode and break off this asteroid + redef fun destroy + do + super + + world.asteroids.remove self + + if size == 1 then return # Do not break off + + for p in world.n_asteroid_parts.times do + var asteroid = new Asteroid.break_off(self) + asteroid.size = size - 1 + asteroid.color = color + + asteroid.inertia.x += 1.0 - 2.0.rand + asteroid.inertia.y += 1.0 - 2.0.rand + + world.objects.add asteroid + world.asteroids.add asteroid + end + end +end + +# Bullet or beam fired from a `Ship` +class Bullet + super SpacialObject + + # Time left before this bullet expires + var ttl = 5.0 + + init do radius = 0.0 + + redef fun do_turn(dt) + do + super + + ttl -= dt + if ttl <= 0.0 then destroy + end + + redef fun destroy + do + super + world.bullets.remove self + end +end diff --git a/lib/gamnit/examples/asteronits/src/texture_atlas_parser.nit b/lib/gamnit/examples/asteronits/src/texture_atlas_parser.nit new file mode 100644 index 0000000..113b887 --- /dev/null +++ b/lib/gamnit/examples/asteronits/src/texture_atlas_parser.nit @@ -0,0 +1,117 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Tool to parse XML texture atlas and generated Nit code to access subtextures +module texture_atlas_parser + +import dom +import gen_nit +import opts + +# Command line options +var opts = new OptionContext +var opt_name = new OptionString("Name of the module to generate", "--name", "-n") +var opt_dir = new OptionString("Folder where to write the generated module", "--dir") +opts.add_option(opt_name, opt_dir) +opts.parse +var rest = opts.rest + +if opts.errors.not_empty then + print_error opts.errors + exit 1 +end + +if rest.is_empty then + print_error "Error: Expected the path to the XML file as argument" + exit 2 +end + +# Prepare to read XML file and gather the attributes +var xml_file = rest.first +var attributes = new Array[String] + +# Insert the first attribute, to load the root texture +var png_file = "images" / xml_file.basename("xml") + "png" +attributes.add """ + var root_texture = new Texture("{{{png_file}}}")""" + +# Read XML file +var content = xml_file.to_path.read_all +var xml = content.to_xml +if xml isa XMLError then + print_error "RSS Parse Error: {xml.message}:{xml.location or else "null"}" + exit 3 +end + +var items = xml["TextureAtlas"].first.children +for item in items do if item isa XMLOnelinerTag then + var x = null + var y = null + var width = null + var height = null + var name = null + for attr in item.attributes do if attr isa XMLStringAttr then + if attr.name == "x" then + x = attr.value.to_i + else if attr.name == "y" then + y = attr.value.to_i + else if attr.name == "width" then + width = attr.value.to_i + else if attr.name == "height" then + height = attr.value.to_i + else if attr.name == "name" then + name = attr.value + end + end + + if x != null and y != null and width != null and height != null and name != null then + name = name.strip_extension(".png").to_snake_case + + var coords = "{x}, {y}, {width}, {height}" + attributes.add """ + var {{{name}}}: Texture = root_texture.subtexture({{{coords}}})""" + else + print_error "Error on {item}" + end +end + +var module_name = opt_name.value +if module_name == null then module_name = "spritesheet" +var class_name = module_name.capitalized.to_camel_case + +# Generate Nit code +var nit_module = new NitModule(module_name) +nit_module.header = """ +# This file is generated by texture_atlas_parser +""" + +nit_module.content.add """ +import gamnit::display +import gamnit::textures + +class {{{class_name}}} +""" + +for a in attributes do nit_module.content.add a + +nit_module.content.add """ +end""" + +var dir = opt_dir.value +if dir != null then + var out_path = dir / module_name + ".nit" + nit_module.write_to_file out_path +else + printn nit_module.write_to_string +end diff --git a/lib/gamnit/examples/asteronits/src/touch_ui.nit b/lib/gamnit/examples/asteronits/src/touch_ui.nit new file mode 100644 index 0000000..91ce2be --- /dev/null +++ b/lib/gamnit/examples/asteronits/src/touch_ui.nit @@ -0,0 +1,117 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Touchscreen UI for mobile devices +module touch_ui + +import asteronits +import controls + +redef class App + + # Controls texture + var spritesheet_controls = new ControlsImages + + private var joystick_x = 200.0 + private var joystick_y = 100.0 + + redef fun accept_event(event) + do + super + + var display = display + if display == null then return false + + if event isa PointerEvent and not event.is_move then + + # Convert input coordinates from screen coordinates to UI units. + # Effectively reverting the transformation created by `ui_camera.reset_height`. + var ui_pos = ui_camera.camera_to_ui(event.x, event.y) + + var ship = world.ship + + if ui_pos.y.to_i > display.height/2 then + # Lower half of the screen + if ui_pos.x.to_i > display.width/2 then + # Bottom right + if event.pressed then ship.fire + else + # Bottom left + var dx = ui_pos.x - joystick_x + var dy = ui_pos.y - (ui_camera.height - joystick_y) + + # Any touch in the joystick reset all joystick effects. + # It prevents leaving a button without releasing by moving + # the pointer over another button. + ship.applied_rotation = 0.0 + ship.applied_thrust = 0.0 + + if not event.pressed then return true + + if dy > 0.0 then + # Bottom part of the joystick, turns left or right + if dx < 0.0 then + ship.applied_rotation = -1.0 + else + ship.applied_rotation = 1.0 + end + else + # Upper part of the joystick, detect action using 45d angles + if dx < dy then + ship.applied_rotation = -1.0 + else if dx > -dy then + ship.applied_rotation = 1.0 + else + ship.applied_thrust = 1.0 + end + end + end + end + return true + end + + return false + end + + redef fun on_create + do + super + + var display = display + assert display != null + + # Standardize the UI size to look like a 1600 pixels high screen. + # Meaning that the controls have a size proportional to the height of each screen. + # In the code, we can use "pixel" precision as if the target screen was 1600 pixels high. + ui_camera.reset_height 1600.0 + + # Add the joystick to the UI + ui_sprites.add new Sprite(spritesheet_controls.forward, + ui_camera.bottom_left.offset(joystick_x, -200.0, 0.0)) + ui_sprites.add new Sprite(spritesheet_controls.left, + ui_camera.bottom_left.offset(joystick_x-100.0, -joystick_y, 0.0)) + ui_sprites.add new Sprite(spritesheet_controls.right, + ui_camera.bottom_left.offset(joystick_x+100.0, -joystick_y, 0.0)) + + # Purely cosmetic joystick background + ui_sprites.add new Sprite(spritesheet_controls.joystick_back, + ui_camera.bottom_left.offset(joystick_x, -joystick_y, -1.0)) # In the back + ui_sprites.add new Sprite(spritesheet_controls.joystick_down, + ui_camera.bottom_left.offset(joystick_x, 0.0, 1.0)) + + # Add the "open fire" button + ui_sprites.add new Sprite(spritesheet_controls.fire, + ui_camera.bottom_right.offset(-150.0, -150.0, 0.0)) + end +end