В последней части вы увидели, как вы можете создавать спрайтов, анимировать их и придавать им поведение. Но анимация не очень хорошая, потому что, как Insectoid, вы должны всегда смотреть туда, куда летите. Помните: безопасность прежде всего! Мы можем сделать это очень легко, создав собственную TileSetAnimation:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public class RotatingTileSetAnimation extends TileSetAnimation { private double angle = 0 ; public RotatingTileSetAnimation(TileSet set, int [] indices, float speed) { super (set, indices, speed); } public void setAngle( double angle) { this .angle = angle; } @Override public void render(Sprite sprite, GraphicsContext context, float alpha, long delta) { context.save(); context.translate(sprite.getWidth() / 2 , sprite.getHeight() / 2 ); context.rotate(angle); context.translate(-sprite.getWidth() / 2 , -sprite.getHeight() / 2 ); super .render(sprite, context, alpha, delta); //To change body of generated methods, choose Tools | Templates. context.restore(); } } |
Мы можем вычислить угол поворота по скорости x и y и установить его в нашем GraphicsContext перед рендерингом. Итак, вот подкласс, который делает это:
01
02
03
04
05
06
07
08
09
10
11
12
|
public class LookAheadTileSetAnimation extends RotatingTileSetAnimation { public LookAheadTileSetAnimation(TileSet set, int [] indices, float speed) { super (set, indices, speed); } @Override public void render(Sprite sprite, GraphicsContext context, float alpha, long delta) { setAngle(Math.toDegrees(Math.atan2(sprite.getVelocityY(), sprite.getVelocityX()))); super .render(sprite, context, alpha, delta); //To change body of generated methods, choose Tools | Templates. } } |
Вот результат:
Довольно просто, не правда ли? Теперь следующим шагом будет добавление некоторого поведения к самим туретам. Я хочу, чтобы они всегда проверяли ближайшего врага и наводили на него пушку. Сначала я немного изменил код и снова разделил башни на базы и пушки. Поэтому, когда вы выбираете пушку сейчас, в TileLayer будет размещена турель-база с названием «турели-базы». Я просто изменил класс TurretView для поддержки этого:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
class TileSetView extends StackPane { Canvas canvas; TileSet cannons; TileSet bases; int selectedIndex = 0 ; Color selected = Color.rgb( 100 , 100 , 255 , . 2 ); public TileSetView() { } public void setTileSet( final TileSet bases, final TileSet cannons) { this .cannons = cannons; this .bases = bases; getChildren().clear(); ImageView turretBases = new ImageView(); turretBases.setImage(bases.getTileImage()); ImageView turretCannons = new ImageView(); turretCannons.setImage(cannons.getTileImage()); getChildren().addAll(turretBases, turretCannons); canvas = new Canvas(cannons.getTileImage().getWidth(), cannons.getTileImage().getHeight()); getChildren().add(canvas); canvas.setOnMouseClicked( new EventHandler() { @Override public void handle(MouseEvent t) { double x = t.getX(); double y = t.getY(); selectedIndex = ( int ) (( int ) x / cannons.getTilewidth() + ((( int ) y / cannons.getTileheight()) * cannons.getNumColumns())); updateCanvas(); } }); updateCanvas(); } public int getSelectedGid() { if (bases == null ) { return - 1 ; } return bases.getFirstgid() + selectedIndex; } public int getSelectedIndex(){ return selectedIndex; } public void updateCanvas() { GraphicsContext graphicsContext2D = canvas.getGraphicsContext2D(); graphicsContext2D.clearRect( 0 , 0 , canvas.getWidth(), canvas.getHeight()); if (selectedIndex >= 0 ) { graphicsContext2D.setFill(selected); int x = selectedIndex % cannons.getNumColumns(); int y = selectedIndex / cannons.getNumColumns(); graphicsContext2D.fillRect(x * cannons.getTilewidth(), y * cannons.getTileheight(), cannons.getTilewidth(), cannons.getTileheight()); } } } |
Так вот как это выглядит сейчас:
Далее мы добавим пушки. В то время как базы турелей — это простые плитки, наши пушки должны быть спрайтами, поэтому мы можем добавить к ним поведение:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
public class CannonSprite extends Sprite { RotatingTileSetAnimation rotateAnimation; public CannonSprite(GameCanvas parent, RotatingTileSetAnimation animation, String name, double x, double y, int width, int height) { super (parent, animation, name, x, y, width, height, Lookup.EMPTY); this .rotateAnimation = animation; addBehaviour( new SpriteBehavior() { @Override public boolean perform(Sprite sprite) { Sprite closest = null ; double dist = Double.MAX_VALUE; Collection sprites = sprite.getParent().getSprites(); for (Sprite sprite1 : sprites) { if (sprite1 instanceof EnemySprite) { double distance = distance(getX(), getY(), sprite1.getX(), sprite1.getY()); if (distance < dist) { dist = distance; closest = sprite1; } } } if (closest != null ) { rotateAnimation.setAngle(Math.toDegrees(Math.atan2(closest.getY() - sprite.getY(),closest.getX() - sprite.getX()))); } return true ; } }); } public double distance( double x1, double y1, double x2, double y2) { return Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); } } |
Я снова использую RotatingTileSetAnimation и просто устанавливаю угол так, чтобы пушка указывала на ближайшего врага. Вот что мы получаем:
Вот именно для этой части урока. Мы создали несколько пользовательских анимаций, чтобы заставить Инсектоиды смотреть в правильном направлении, а также чтобы Турели всегда указывали на цель закрытия. Как вы можете видеть из этих примеров, игровой движок пытается упростить добавление поведения к спрайтам. В следующей части мы заставим турели стрелять в своих врагов.