Я люблю собирать прототипы и пробывать разные варианты управления для устройств с touch экраном. Это постоянный поиск чего-то более нативного и привычного, чем джойстик отрисованный поверх геймплея.
Один из таких вариантов – рисование. Но что бы было интереснее я покажу пример рисования с физикой.
Опишу в двух словах, как мы будем это делать:
- Создаем спрайт размером с экран и заполняем его прозрачностью.
- Добавляем к нему пустое физическое тело, в которое потом будем добавлять shape созданные по мотивам нашего рисования
- Ловим в update событие нажатия мыши и записываем координаты нажатия стартовой точки
- Каждый последующий вызов update, если мышь все еще нажата (или палец все еще на экране) считаем расстояние от нашей стартовой точки, до текущей и ждем пока эта величина не станет больше минимально допустимого значения.
- Создаем shape прямоугольной формы с шириной равной отрисованному расстоянию, а высотой 1
- Считаем угол, на который нужно повернуть shape, что бы он был вдоль отрисованной линии и перемещаем тело в координаты стартовой точки
- Добавляем shape к физическому телу созданному в пункте 2.
- Записываем текущие координаты, как координаты стартовой точки
За отрисовку отвечает FlxSpriteUtil и там все просто. Поэтому перейдем к коду:
import flixel.addons.nape.FlxNapeSprite; import flixel.addons.nape.FlxNapeState; import flixel.FlxCamera; import flixel.FlxG; import flixel.ui.FlxButton; import flixel.util.FlxColor; import flixel.util.FlxMath; import flixel.util.FlxPoint; import flixel.util.FlxSpriteUtil; import Math; import nape.constraint.MotorJoint; import nape.constraint.PivotJoint; import nape.geom.Vec2; import nape.phys.Body; import nape.phys.BodyType; import nape.phys.Material; import nape.shape.Polygon; class DrawState extends FlxNapeState { var _canvas:FlxNapeSprite; var _distance:Float; var _angle:Float; var _startPoint:FlxPoint; var _circles:Array<FlxNapeSprite>; static var MINIMUM_LINE_LENGHT:Float = 10; override public function create():Void { super.create(); //Создаем спрайт, в котором будем рисовать _canvas = new FlxNapeSprite(); // Задаем ему размеры равные размеру экрана _canvas.makeGraphic(FlxG.width, FlxG.height, FlxColor.TRANSPARENT); // выставляем точку привязки в 0,0 _canvas.origin.set(0, 0); //добавляем на сцену add(_canvas); //создаем физическое тело var body = new Body(BodyType.KINEMATIC); //Добавляем физическое тело к нашему спрайту _canvas.addPremadeBody(body); _circles = new Array<FlxNapeSprite>(); _startPoint = new FlxPoint(); FlxG.mouse.useSystemCursor = true; createUI(); } function rainOfCircles() { if (FlxNapeState.space.gravity.y != 500) FlxNapeState.space.gravity.setxy(0, 500); var circle:FlxNapeSprite; var RADIUS:Float = 10; napeDebugEnabled = true; for (i in 0...30) { circle = new FlxNapeSprite(i * RADIUS*2 + RADIUS, 0); circle.createCircularBody(RADIUS); circle.makeGraphic(20, 20, FlxColor.TRANSPARENT); FlxSpriteUtil.drawCircle(circle, 10, 10, 10); add(circle); _circles.push(circle); } } override public function update():Void { super.update(); //если кнопка была только что нажата то... if (FlxG.mouse.justPressed) { //копируем координаты нажатия _startPoint.copyFrom(FlxG.mouse); return; } //проверяем нажата ли кнопка мыши if (FlxG.mouse.pressed) { // считаем растояние от места нажатия _distance = FlxMath.getDistance(_startPoint, FlxG.mouse); //отсекаем незначительные движения мыши проверяя растояния от точки нажатия if(_distance > MINIMUM_LINE_LENGHT) { //рисуем линию FlxSpriteUtil.drawLine(_canvas, _startPoint.x, _startPoint.y, FlxG.mouse.x, FlxG.mouse.y, { thickness: 4, color: FlxColor.RED }); //создаем физический прямоугольник var shape:Polygon = new Polygon(Polygon.rect(0, 0, _distance, 1 )); //высчитываем угол поворота прямоугольника _angle = Math.atan2(FlxG.mouse.y - _startPoint.y, FlxG.mouse.x - _startPoint.x); shape.rotate( _angle); //устанавливаем необходимые координаты shape.translate(Vec2.weak(_startPoint.x, _startPoint.y)); //добавляем наш прямоугольник к физическому телу в котором мы рисуем _canvas.body.shapes.add(shape); // запоминаем последние координаты отрисовки, что бы следующий прямоугольник рисовать уже в нужном месте _startPoint.copyFrom(FlxG.mouse); } } for (circle in _circles) { if (!circle.isOnScreen(FlxG.camera)) { //удаляем круги вылетевшие за экран _circles.remove(circle); circle.destroy(); } } } private function reset():Void { FlxG.resetState(); } function napeDebug() { napeDebugEnabled = !napeDebugEnabled; } function createUI() { var btn:FlxButton = new FlxButton(0, 0, "Reset", reset); add(btn); btn = new FlxButton(btn.x + btn.width + 10, 0, "Create Circles", rainOfCircles); add(btn); } }
А вот apk для желающих посмотреть на своем Android: [ddownload id=”41″]
Это не самый производительный способ. Но с поставленной задачей справляется на ура.
В скором будущем постараюсь рассказать, как рисовать с физикой в Unity. А так же про более хитрую и производительную рисовалку на haxeflixel, для более динамичных игр и с зоной рисования не ограниченной одним экраном.