В этом уроке мы создадим игру, целью которой является предотвращение столкновения других объектов с вашим курсором. Мы не будем использовать встроенные в hitTestObject()
методы hitTestObject()
; вместо этого мы напишем наши собственные процедуры обнаружения столкновений.
Каждые несколько недель мы пересматриваем некоторые из любимых постов нашего читателя на протяжении всей истории сайта. Этот учебник был впервые опубликован в феврале 2011 года.
Окончательный результат предварительного просмотра
Давайте посмотрим на конечный результат, к которому мы будем стремиться:
Шаг 1: Начните с
Создайте новый файл Flash (ActionScript 3.0)
Установите размеры сцены на 500x500px и FPS на 32.
Шаг 2: Класс Ball
Этот класс будет содержать все данные, связанные с одним мячом. Мяч имеет _mass
, _radius
, _xSpeed
и _ySpeed
. Поэтому мы сделаем недвижимость для каждого. В конструктор мы передаем массу, угол и скорость мяча. Поскольку класс будет связан с экранным объектом, мы можем получить радиус нашего шара, разделив ширину _xSpeed
объекта на 2. _xSpeed
и _ySpeed
можно рассчитать с помощью простых функций синуса и косинуса.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package
{
import flash.display.Stage
import flash.display.Sprite
import flash.events.Event
public class Ball extends Sprite
{
private var _radius:Number = 0
private var _mass:Number = 0
private var _xSpeed:Number = 0
private var _ySpeed:Number = 0
public function Ball(mass:Number = 10.0, angle:Number = Math.PI, speed:Number = 10.0):void
{
this.mass = mass
this._radius = this.width/2
this.xSpeed = speed*Math.sin(angle)
this.ySpeed = speed*Math.cos(angle)
}
}
}
|
Для получения дополнительной информации об этих тригонометрических функциях Math.sin () и Math.cos () см. Этот Быстрый совет .
Шаг 3: Предоставление геттеров и сеттеров
В нашем классе Ball мы предоставляем геттеры и сеттеры для наших свойств.
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
|
public function get radius():Number
{
return this._radius
}
public function set mass(mass:Number):void
{
this._mass = mass
}
public function get mass():Number
{
return this._mass
}
public function set xSpeed(xSpeed:Number):void
{
this._xSpeed = xSpeed
}
public function get xSpeed():Number
{
return this._xSpeed
}
public function set ySpeed(ySpeed:Number):void
{
this._ySpeed = ySpeed
}
public function get ySpeed():Number
{
return this._ySpeed
}
|
Шаг 4: функция обновления
Эта функция обновляет свойства x и y нашего шара в соответствии с _xSpeed
и _ySpeed
. Мы реализуем эту функцию в нашем классе Ball
.
1
2
3
4
5
|
public function update():void
{
this.x += _xSpeed
this.y += _ySpeed
}
|
Шаг 5: Завершенный класс
На этом мы закончим наш урок по Ball
.
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
60
61
62
63
|
package
{
import flash.display.Stage
import flash.display.Sprite
import flash.events.Event
public class Ball extends Sprite
{
private var _radius:Number = 0
private var _mass:Number = 0
private var _xSpeed:Number = 0
private var _ySpeed:Number = 0
public function Ball(mass:Number = 10.0, angle:Number = Math.PI, speed:Number = 10.0):void
{
this.mass = mass
this._radius = this.width/2
this.xSpeed = speed*Math.sin(angle)
this.ySpeed = speed*Math.cos(angle)
}
public function get radius():Number
{
return this._radius
}
public function set mass(mass:Number):void
{
this._mass = mass
}
public function get mass():Number
{
return this._mass
}
public function set xSpeed(xSpeed:Number):void
{
this._xSpeed = xSpeed
}
public function get xSpeed():Number
{
return this._xSpeed
}
public function set ySpeed(ySpeed:Number):void
{
this._ySpeed = ySpeed
}
public function get ySpeed():Number
{
return this._ySpeed
}
public function update():void
{
this.x += _xSpeed
this.y += _ySpeed
}
}
}
|
Шаг 6: Показать объекты для нашего класса Ball
В исходные файлы я включил стартовый FLA, который содержит все необходимые элементы библиотеки. Вы можете нарисовать их сами, если хотите, конечно. Убедитесь, что ваш FLA имеет следующие экранные объекты:
(Примечание редактора: это опечатка: «ennemyball» должен сказать «вражеский шар».)
Шаг 7: Связывание объектов нашей библиотеки
Класс Ball
мы только что создали, должен быть связан со Sprite enemyball
в библиотеке.
Спрайт PlayerBall
должен иметь Ball
качестве базового класса и PlayerBall
качестве класса.
Клип с score
должен иметь класс Score
.
Шаг 8: Класс приложения (Класс документа)
Класс Application
будет содержать всю игровую логику. Мы импортируем все классы, которые нам нужны. Как вы можете видеть, мы будем использовать TweenMax .
Далее мы определяем наши переменные поля. Первая переменная поля — это ballPlayer
.
Поскольку базовым классом нашего спрайта ballPlayer
является Ball
мы можем сохранить этот класс в переменной ballPlayer
. Это облегчает проверку позже столкновения между ballPlayer
и вражескими шарами.
Вторая переменная поля — это массив, который будет содержать все наши вражеские шары. Третья переменная — это таймер, который будет использоваться для выполнения основного игрового цикла. Четвертое и последнее поле — это экземпляр объекта библиотеки Score
который будет использоваться для отображения прошедшего игрового времени. В конструкторе мы вызываем функцию init()
которую я объясню на следующем шаге.
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
|
package
{
import flash.display.Sprite
import flash.display.Graphics
import flash.events.Event
import flash.events.TimerEvent
import flash.events.MouseEvent
import flash.geom.Matrix
import flash.utils.Timer
import flash.ui.Mouse
import com.greensock.TweenMax
import com.greensock.easing.*
public class Application extends Sprite
{
private var ballPlayer:Ball
private var eballs:Array
private var tmr:Timer
private var score:Score
public function Application():void
{
init()
}
}
}
|
Не забудьте связать класс документа !
Шаг 9: Функция init ()
Посмотрите на этот код:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
private function init():void
{
ballPlayer = new PlayerBall()
eballs = new Array()
tmr = new Timer(10)
score = new Score()
stage.align = «TL»
stage.scaleMode = «noScale»
Mouse.hide()
setBackground()
score.x = stage.stageWidth/2
score.y = stage.stageHeight/2
stage.addChild(score)
stage.addEventListener(MouseEvent.MOUSE_MOVE, updatePlayerBall)
stage.addChild(ballPlayer)
tmr.addEventListener(TimerEvent.TIMER, updateTime)
stage.addEventListener(MouseEvent.CLICK, startGame)
}
|
В первых четырех строках мы инициализируем наши переменные поля.
Затем мы убедимся, что наша сцена выровнена по верхнему левому углу и не масштабируется.
Мы прячем курсор мыши. Наш курсор будет заменен на playerball
Sprite. Далее мы вызываем функцию setBackground
(объяснено на следующем шаге).
Мы центрируем наш score
на экране и добавляем его в список отображения. Чтобы обновить позицию ballPlayer
мы присоединяем событие MouseEvent.MOUSE_MOVE к сцене.
Функция updatePlayerBall
(объясненная на шаге 11) будет обрабатывать этот MouseEvent. Затем мы добавляем ballPlayer
в список отображения.
Таймер будет использоваться для отображения игрового времени. Мы присоединяем слушатель TimerEvent.TIMER к нашему таймеру, который будет запускать updateTime()
(объяснено в шаге 12) каждые 10 миллисекунд.
Наконец, мы добавляем MouseEvent.CLICK на нашу сцену. Функция startGame
(объясненная на шаге 13) запустит нашу игру.
Шаг 10: функция setBackground ()
Эта функция добавляет радиальный градиентный фон в список отображения. Чтобы нарисовать градиент на спрайте, вы должны определить тип градиента, цвета, которые вы хотите использовать, альфа-значения цветов, соотношения (они определяют распределение цветов) и метод распространения.
Для получения дополнительной информации см. Этот Быстрый совет по градиентам .
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
private function setBackground():void
{
var type:String = «radial»
var colors:Array = [0xffffff,0xcccccc]
var alphas:Array = [ 1, 1 ]
var ratios:Array = [ 0, 255 ]
var matr:Matrix = new Matrix()
matr.createGradientBox(stage.stageWidth, stage.stageHeight, Math.PI / 2, 0, 0 )
//SpreadMethod will define how the gradient is spread.
var sprMethod:String = «pad»
//Start the Gradietn and pass our variables to it
var sprite:Sprite = new Sprite()
//Save typing + increase performance through local reference to a Graphics object
var g:Graphics = sprite.graphics
g.beginGradientFill( type, colors, alphas, ratios, matr, sprMethod )
g.drawRect(0,0,stage.stageWidth,stage.stageHeight)
stage.addChild(sprite)
}
|
Шаг 11: функция updatePlayerBall ()
Эта функция обновляет положение ballPlayer
соответствии с положением вашей мыши.
1
2
3
4
5
|
private function updatePlayerBall(e:MouseEvent):void
{
ballPlayer.x = mouseX
ballPlayer.y = mouseY
}
|
Шаг 12: функция updateTime ()
Мы рассчитываем время в секундах и помещаем его в текстовое поле нашего score
Sprite. Каждые 5000 мс (пять секунд) мы добавляем новый мяч в игру.
1
2
3
4
5
6
7
8
|
private function updateTime(e:TimerEvent):void
{
score.txtScore.text = String(((tmr.currentCount*tmr.delay)/1000).toFixed(2));
if((tmr.currentCount*tmr.delay) % 5000 == 0)
{
addBall();
}
}
|
Шаг 13: функция startGame ()
Игра запускается нажатием на сцену. Сначала мы удаляем слушателя для щелчка по сцене, чтобы мы не могли начать игру несколько раз. Мы добавляем в игру три мяча, трижды вызывая addBall()
(объясняется в следующем шаге). Мы запускаем наш таймер, который обновит наше игровое время.
Наконец, мы добавляем событие ENTER_FRAME на нашу сцену. Функция gameLoop()
(объясненная в шаге 15) будет обновлять положение наших вражеских шаров.
1
2
3
4
5
6
7
8
9
|
private function startGame(e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.CLICK, startGame)
addBall()
addBall()
addBall()
tmr.start()
stage.addEventListener(Event.ENTER_FRAME, gameLoop)
}
|
Шаг 14: функция addBall ()
Сначала мы создаем новый экземпляр нашего класса Ball
. Мы размещаем ball
случайным образом на сцене с альфа 0 и добавляем его в список отображения.
Затем мы вернем альфу обратно в 1. (Я использую TweenMax, он включен в исходные файлы. Вы также можете использовать встроенный движок анимации Flash.) Вторая анимация на самом деле не анимация. Он просто ждет секунду, и функция onComplete
eballs
ball
в наш массив eballs
. Таким образом, gameLoop()
(объясненная на следующем шаге) может обрабатывать все остальное.
01
02
03
04
05
06
07
08
09
10
|
private function addBall():void
{
var ball:Ball = new Ball(10, Math.random()*Math.PI*2, 5)
ball.x = Math.random()*stage.stageWidth
ball.y = Math.random()*stage.stageHeight
ball.alpha = 0
stage.addChild(ball)
TweenMax.to(ball, 0.5, {alpha:1})
TweenMax.to(ball, 0, {delay: 1, onComplete:function():void{eballs.push(ball)}})
}
|
Шаг 15: функция gameLoop ()
Каждый кадр пройдет через эту функцию.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
private function gameLoop(e:Event):void
{
for (var i:uint = 0; i < eballs.length; i++)
{
for (var j:uint = i + 1; j < eballs.length; j++)
{
if (collision(eballs[i], eballs[j]))
{
doCollision(eballs[i], eballs[j])
}
}
if(collision(eballs[i], ballPlayer))
{
endOfGame()
break
}
eballs[i].update()
checkBounds(eballs[i])
}
}
|
Мы начинаем с перебора всех наших вражеских шаров.
Второй цикл for проверяет наличие столкновений между вражескими шарами. Цикл начинается с «i + 1». Таким образом, мы не проверяем дважды столкновения.
Затем мы проверяем, поражает ли ballPlayer
вражеский шар. Если так, игра закончена. Затем мы обновляем позицию нашего вражеского мяча.
Мы уверены, что шары остаются на игровом экране, вызывая функцию checkBounds()
(объяснено позже).
Шаг 16: функция столкновения ()
Эта функция проверяет, сталкивается ли любая данная пара шаров.
Сначала мы вычисляем расстояние x и расстояние y между двумя шарами. Используя теорему Пифагора (см. Следующую диаграмму), мы вычисляем абсолютное расстояние между ними. Если расстояние меньше или равно сумме радиусов шаров, мы сталкиваемся.
1
2
3
4
5
6
7
8
|
private function collision(ball1:Ball, ball2:Ball):Boolean
{
var xDist:Number = ball1.x — ball2.x
var yDist:Number = ball1.y — ball2.y
var Dist:Number = Math.sqrt(xDist * xDist + yDist * yDist)
return Dist <= ball1.radius + ball2.radius
}
|
Шаг 17: функция doCollision ()
Эта функция будет вычислять новые x- и y-скорости шариков в соответствии со скоростью и углом столкновения. Предупреждение: математика;)
Сначала мы рассчитываем горизонтальное расстояние между двумя шарами, а затем вертикальное расстояние между шарами. С этими расстояниями (и немного большей тригонометрией ) мы можем вычислить угол между шарами (см. Диаграмму).
Далее мы вычисляем то, что я называю величиной каждого шара. (У нас есть вектор xspeed и вектор yspeed; величина является векторной суммой этих значений.) Затем мы вычисляем угол каждого шара (аналогично предыдущему вычислению угла).
Затем мы вращаем новые x- и y-скорости каждого шара. На самом деле мы вращаем систему координат. Вращая наши оси, мы сталкиваемся с 1D. (См. Следующую диаграмму).
Ньютон говорит, что общее количество кинетической энергии в замкнутой системе постоянно. Теперь мы используем эти формулы:
-
v1 = (u1*(m1-m2) + 2*m2*u2)/(m1+m2)
-
v2 = (u2*(m2-m1) + 2*m1*u1)/(m1+m2)
где:
v1 = окончательный xSpeedBall 1
v2 = окончательный xSpeedBall 2
m1 = масса мяча 1
м2 = масса шара 2
u1 = начальная скорость мяча 1
u2 = начальная скорость мяча 2
Y-скорости не меняются, так как это одномерное столкновение.
С помощью этих формул мы можем рассчитать xSpeed
и ySpeed
каждого шара.
Теперь у нас есть новые x- и y-скорости в нашей повернутой системе координат. Последний шаг — преобразовать все обратно в нормальную систему координат. Мы используем Math.PI/2
потому что угол между xSpeed
и ySpeed
всегда должен быть 90 градусов (пи / 2 радиана ).
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
|
private function doCollision(ball1:Ball, ball2:Ball):void
{
var xDist:Number = ball1.x — ball2.x
var yDist:Number = ball1.y — ball2.y
var collisionAngle:Number = Math.atan2(yDist, xDist)
var magBall1:Number = Math.sqrt(ball1.xSpeed*ball1.xSpeed+ball1.ySpeed*ball1.ySpeed)
var magBall2:Number = Math.sqrt(ball2.xSpeed*ball2.xSpeed+ball2.ySpeed*ball2.ySpeed)
var angleBall1:Number = Math.atan2(ball1.ySpeed, ball1.xSpeed)
var angleBall2:Number = Math.atan2(ball2.ySpeed, ball2.xSpeed)
var xSpeedBall1:Number = magBall1 * Math.cos(angleBall1-collisionAngle)
var ySpeedBall1:Number = magBall1 * Math.sin(angleBall1-collisionAngle)
var xSpeedBall2:Number = magBall2 * Math.cos(angleBall2-collisionAngle)
var ySpeedBall2:Number = magBall2 * Math.sin(angleBall2-collisionAngle)
var finalxSpeedBall1:Number = ((ball1.mass-ball2.mass)*xSpeedBall1+(ball2.mass+ball2.mass)*xSpeedBall2)/(ball1.mass+ball2.mass)
var finalxSpeedBall2:Number = ((ball1.mass+ball1.mass)*xSpeedBall1+(ball2.mass-ball1.mass)*xSpeedBall2)/(ball1.mass+ball2.mass)
var finalySpeedBall1:Number = ySpeedBall1
var finalySpeedBall2:Number = ySpeedBall2
ball1.xSpeed = Math.cos(collisionAngle)*finalxSpeedBall1+Math.cos(collisionAngle+Math.PI/2)*finalySpeedBall1
ball1.ySpeed = Math.sin(collisionAngle)*finalxSpeedBall1+Math.sin(collisionAngle+Math.PI/2)*finalySpeedBall1
ball2.xSpeed = Math.cos(collisionAngle)*finalxSpeedBall2+Math.cos(collisionAngle+Math.PI/2)*finalySpeedBall2
ball2.ySpeed = Math.sin(collisionAngle)*finalxSpeedBall2+Math.sin(collisionAngle+Math.PI/2)*finalySpeedBall2
}
|
Чтобы найти больше информации об упругих столкновениях, взгляните на hoomanr.com .
Шаг 18: функция endOfGame ()
Это запускается, когда игра заканчивается.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
private function endOfGame():void
{
tmr.stop()
Mouse.show()
stage.removeEventListener(MouseEvent.MOUSE_MOVE, updatePlayerBall)
stage.removeEventListener(Event.ENTER_FRAME, gameLoop)
while(eballs.length > 0)
{
TweenMax.to(eballs[0], 0.5, {scaleX:0, scaleY:0, ease:Bounce.easeOut})
eballs.splice(0,1)
}
TweenMax.to(ballPlayer, 0.5, {scaleX:0, scaleY:0, ease:Bounce.easeOut})
}
|
Прежде всего остановим таймер. Мы снова показываем мышь. Затем мы удаляем прослушиватели событий MOUSE_MOVE и ENTER_FRAME. Наконец мы делаем все шары на сцене невидимыми.
Шаг 19: функция checkBounds ()
Эта функция гарантирует, что шары остаются внутри игрового экрана. Поэтому, если мяч попадает в верхнюю или нижнюю сторону, мы ySpeed
. если мяч попадает в левую или правую часть экрана, мы xSpeed
. Он использует логику, аналогичную функции обнаружения столкновения мяча, чтобы проверить, попадают ли края шара на край экрана.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
private function checkBounds(ball:Ball):void
{
if((ball.x + ball.radius) > stage.stageWidth)
{
ball.x = stage.stageWidth — ball.radius
ball.xSpeed *= -1
}
if((ball.x — ball.radius) < 0)
{
ball.x = 0 + ball.radius
ball.xSpeed *= -1
}
if((ball.y + ball.radius) > stage.stageHeight)
{
ball.y = stage.stageHeight — ball.radius
ball.ySpeed *= — 1
}
if((ball.y — ball.radius) < 0)
{
ball.y = 0 + ball.radius
ball.ySpeed *= — 1
}
}
|
Шаг 20: Полный класс приложения
Мы завершили наш класс приложений. Теперь у нас есть рабочая игра !!!
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
|
package
{
import flash.display.Sprite;
import flash.display.Graphics;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.utils.Timer;
import flash.ui.Mouse;
import com.greensock.TweenMax;
import com.greensock.easing.*;
public class Application extends Sprite
{
private var ballPlayer:Ball;
private var eballs:Array;
private var tmr:Timer;
private var score:Score;
public function Application():void
{
init();
}
private function init():void
{
ballPlayer = new PlayerBall();
eballs = new Array();
tmr = new Timer(10);
score = new Score();
stage.align = «TL»;
stage.scaleMode = «noScale»;
Mouse.hide();
setBackground();
score.x = stage.stageWidth / 2;
score.y = stage.stageHeight / 2;
stage.addChild(score);
stage.addEventListener(MouseEvent.MOUSE_MOVE, updatePlayerBall);
stage.addChild(ballPlayer);
tmr.addEventListener(TimerEvent.TIMER, updateTime);
stage.addEventListener(MouseEvent.CLICK, startGame);
}
private function setBackground():void
{
var type:String = «radial»;
var colors:Array = [0xffffff,0xcccccc];
var alphas:Array = [1,1];
var ratios:Array = [0,255];
var matr:Matrix = new Matrix();
matr.createGradientBox(stage.stageWidth, stage.stageHeight, Math.PI / 2, 0, 0 );
//SpreadMethod will define how the gradient is spread.
var sprMethod:String = «pad»;
//Start the Gradietn and pass our variables to it
var sprite:Sprite = new Sprite();
//Save typing + increase performance through local reference to a Graphics object
var g:Graphics = sprite.graphics;
g.beginGradientFill( type, colors, alphas, ratios, matr, sprMethod );
g.drawRect(0,0,stage.stageWidth,stage.stageHeight);
stage.addChild(sprite);
}
private function updatePlayerBall(e:MouseEvent):void
{
ballPlayer.x = mouseX;
ballPlayer.y = mouseY;
}
private function updateTime(e:TimerEvent):void
{
score.txtScore.text = String(((tmr.currentCount*tmr.delay)/1000).toFixed(2));
if ((tmr.currentCount*tmr.delay) % 5000 == 0)
{
addBall();
}
}
private function startGame(e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.CLICK, startGame);
addBall();
addBall();
addBall();
tmr.start();
stage.addEventListener(Event.ENTER_FRAME, gameLoop);
}
private function addBall():void
{
var ball:Ball = new Ball(10,Math.random() * Math.PI * 2,5);
ball.x = Math.random() * stage.stageWidth;
ball.y = Math.random() * stage.stageHeight;
ball.alpha = 0;
stage.addChild(ball);
TweenMax.to(ball, 0.5, {alpha:1});
TweenMax.to(ball, 0, {delay: 1, onComplete:function():void{eballs.push(ball)}});
}
private function gameLoop(e:Event):void
{
for (var i:uint = 0; i < eballs.length; i++)
{
for (var j:uint = i + 1; j < eballs.length; j++)
{
if (collision(eballs[i],eballs[j]))
{
doCollision(eballs[i], eballs[j]);
}
}
if (collision(eballs[i],ballPlayer))
{
endOfGame();
break;
}
eballs[i].update();
checkBounds(eballs[i]);
}
}
private function collision(ball1:Ball, ball2:Ball):Boolean
{
var xDist:Number = ball1.x — ball2.x;
var yDist:Number = ball1.y — ball2.y;
var Dist:Number = Math.sqrt(xDist * xDist + yDist * yDist);
if (Dist <= ball1.radius + ball2.radius)
{
if (ball1.x < ball2.x)
{
ball1.x -= 2;
ball2.x += 2;
}
else
{
ball1.x += 2;
ball2.x -= 2;
}
if (ball1.y < ball2.y)
{
ball1.y -= 2;
ball2.y += 2;
}
else
{
ball1.y += 2;
ball2.y -= 2;
}
}
return Dist <= ball1.radius + ball2.radius;
}
private function doCollision(ball1:Ball, ball2:Ball):void
{
var xDist:Number = ball1.x — ball2.x;
var yDist:Number = ball1.y — ball2.y;
var collisionAngle:Number = Math.atan2(yDist,xDist);
var magBall1:Number = Math.sqrt(ball1.xSpeed * ball1.xSpeed + ball1.ySpeed * ball1.ySpeed);
var magBall2:Number = Math.sqrt(ball2.xSpeed * ball2.xSpeed + ball2.ySpeed * ball2.ySpeed);
var angleBall1:Number = Math.atan2(ball1.ySpeed,ball1.xSpeed);
var angleBall2:Number = Math.atan2(ball2.ySpeed,ball2.xSpeed);
var xSpeedBall1:Number = magBall1 * Math.cos(angleBall1 — collisionAngle);
var ySpeedBall1:Number = magBall1 * Math.sin(angleBall1 — collisionAngle);
var xSpeedBall2:Number = magBall2 * Math.cos(angleBall2 — collisionAngle);
var ySpeedBall2:Number = magBall2 * Math.sin(angleBall2 — collisionAngle);
var finalxSpeedBall1:Number = ((ball1.mass-ball2.mass)*xSpeedBall1+(ball2.mass+ball2.mass)*xSpeedBall2)/(ball1.mass+ball2.mass);
var finalxSpeedBall2:Number = ((ball1.mass+ball1.mass)*xSpeedBall1+(ball2.mass-ball1.mass)*xSpeedBall2)/(ball1.mass+ball2.mass);
var finalySpeedBall1:Number = ySpeedBall1;
var finalySpeedBall2:Number = ySpeedBall2;
ball1.xSpeed = Math.cos(collisionAngle) * finalxSpeedBall1 + Math.cos(collisionAngle + Math.PI / 2) * finalySpeedBall1;
ball1.ySpeed = Math.sin(collisionAngle) * finalxSpeedBall1 + Math.sin(collisionAngle + Math.PI / 2) * finalySpeedBall1;
ball2.xSpeed = Math.cos(collisionAngle) * finalxSpeedBall2 + Math.cos(collisionAngle + Math.PI / 2) * finalySpeedBall2;
ball2.ySpeed = Math.sin(collisionAngle) * finalxSpeedBall2 + Math.sin(collisionAngle + Math.PI / 2) * finalySpeedBall2;
}
private function endOfGame():void
{
tmr.stop();
Mouse.show();
stage.removeEventListener(MouseEvent.MOUSE_MOVE, updatePlayerBall);
stage.removeEventListener(Event.ENTER_FRAME, gameLoop);
while (eballs.length > 0)
{
TweenMax.to(eballs[0], 0.5, {scaleX:0, scaleY:0, ease:Bounce.easeOut});
eballs.splice(0,1);
}
TweenMax.to(ballPlayer, 0.5, {scaleX:0, scaleY:0, ease:Bounce.easeOut});
}
private function checkBounds(ball:Ball):void
{
if ((ball.x + ball.radius) > stage.stageWidth)
{
ball.x = stage.stageWidth — ball.radius;
ball.xSpeed *= -1;
}
if ((ball.x — ball.radius) < 0)
{
ball.x = 0 + ball.radius;
ball.xSpeed *= -1;
}
if ((ball.y + ball.radius) > stage.stageHeight)
{
ball.y = stage.stageHeight — ball.radius;
ball.ySpeed *= -1;
}
if ((ball.y — ball.radius) < 0)
{
ball.y = 0 + ball.radius;
ball.ySpeed *= -1;
}
}
}
}
|
Вывод
Вот именно для этого урока. Конечно, вы можете добавить возможность перезапустить игру, но это не должно быть слишком сложно. Этот базовый пример упругих столкновений можно использовать для больших игр, таких как игра в бильярд или подобное.
Надеюсь, вам понравился этот урок, спасибо за чтение!