国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
Flash基礎(chǔ)理論課 第八章 緩動(dòng)與彈性運(yùn)動(dòng)Ⅲ

返回“Flash基礎(chǔ)理論課 - 目錄”

彈簧

下面我們將幾個(gè)彈性小球串聯(lián)起來(lái)。在介紹緩動(dòng)一節(jié)時(shí),我們簡(jiǎn)單地討論了鼠標(biāo)跟隨的概念,意思是說一個(gè)物體跟隨鼠標(biāo),另一個(gè)物體再跟隨這個(gè)物體,依此類推。當(dāng)時(shí)沒有給大家舉例子,是因?yàn)檫@個(gè)效果現(xiàn)在看來(lái)有些遜色。但是,當(dāng)我們?cè)趶椥赃\(yùn)動(dòng)中使用這個(gè)概念時(shí),效果就截然不同了。

本程序的設(shè)計(jì)思想:創(chuàng)建三個(gè)小球,名為ball0,ball1,ball2。第一個(gè)小球,ball0的動(dòng)作與上面例子中的效果是相同的。ball1向ball0 運(yùn)動(dòng),ball2向ball1 運(yùn)動(dòng)。每個(gè)小球都受到重力的影響,所以它們都會(huì)向下墜。代碼稍有些復(fù)雜,文檔類 Chain.as:

package {
 import flash.display.Sprite;
 import flash.events.Event;
 public class Chain extends Sprite {
  private var ball0:Ball;
  private var ball1:Ball;
  private var ball2:Ball;
  private var spring:Number = 0.1;
  private var friction:Number = 0.8;
  private var gravity:Number = 5;
  public function Chain() {
   init();
  }
  private function init():void {
   ball0 = new Ball(20);
   addChild(ball0);
   ball1 = new Ball(20);
   addChild(ball1);
   ball2 = new Ball(20);
   addChild(ball2);
   addEventListener(Event.ENTER_FRAME,onEnterFrame);
  }
  private function onEnterFrame(event:Event):void {
   moveBall(ball0,mouseX,mouseY);
   moveBall(ball1,ball0.x,ball0.y);
   moveBall(ball2,ball1.x,ball1.y);
   graphics.clear();
   graphics.lineStyle(1);
   graphics.moveTo(mouseX,mouseY);
   graphics.lineTo(ball0.x,ball0.y);
   graphics.lineTo(ball1.x,ball1.y);
   graphics.lineTo(ball2.x,ball2.y);
  }
  private function moveBall(ball:Ball,targetX:Number,targetY:Number):void {
   ball.vx += (targetX - ball.x) * spring;
   ball.vy += (targetY - ball.y) * spring;
   ball.vy += gravity;
   ball.vx *= friction;
   ball.vy *= friction;
   ball.x += ball.vx;
   ball.y += ball.vy;
  }
 }
}

看一下 Ball 這個(gè)類,我們發(fā)現(xiàn)每個(gè)對(duì)象實(shí)例都有自己 vx 和 vy 屬性,并且它們的初始值均為0。所以在init方法中,我們只需要?jiǎng)?chuàng)建小球并把它們加入顯示列表。

然后在onEnterFrame函數(shù)中,實(shí)現(xiàn)彈性運(yùn)動(dòng)。這里我們調(diào)用了 moveBall方法,比復(fù)制三次運(yùn)動(dòng)代碼要好用得多。該函數(shù)的參數(shù)分別為一個(gè) ball 對(duì)象以及目標(biāo)點(diǎn)的 x,y 坐標(biāo)。每個(gè)小球都調(diào)用這個(gè)函數(shù),第一個(gè)小球以鼠標(biāo)的 x,y作為目標(biāo)位置,第二第三個(gè)小球以第一第二個(gè)小球作為目標(biāo)位置。

最后,在確定了所有小球的位置后,開始畫線,畫線的起點(diǎn)是鼠標(biāo)位置,然后依次畫到每個(gè)小球上,這樣橡皮圈就連接上了所有的小球。注意,程序中的 friction 降為0.8 為了使小球能夠很快穩(wěn)定下來(lái)。

創(chuàng)建一個(gè)數(shù)組保存鏈中所有對(duì)象的引用,然后通過循環(huán)遍歷數(shù)組中的每個(gè)小球并執(zhí)行運(yùn)動(dòng),使這個(gè)程序更加靈活。這里只需要做一些小小的改變。首先,需要兩個(gè)新的變量代表數(shù)組和對(duì)象數(shù)目:

private var balls:Array;

private var numBalls:Number = 5;

函數(shù) init 中,使用for循環(huán)創(chuàng)建所有對(duì)象,并將對(duì)象引用加入數(shù)組:

private function init():void {
balls = new Array();
    for(var i:uint = 0; i < numBalls; i++) {
        var ball:Ball = new Ball(20);
        addChild(ball);
        balls.push(ball);
    }
    addEventListener(Event.ENTER_FRAME,onEnterFrame);
}

最后,onEnterFrame方法的變化最大。首先設(shè)置線條,將繪圖起點(diǎn)移動(dòng)到鼠標(biāo)位置,再到第一個(gè)小球,然后循環(huán)為剩下的小球設(shè)置位置并連線。通過改變 numBalls 變量,我們可以加入任意多個(gè)小球。

private function onEnterFrame(event:Event):void {
    graphics.clear();
    graphics.lineStyle(1);
    graphics.moveTo(mouseX,mouseY);
    moveBall(balls[0],mouseX,mouseY);
    graphics.lineTo(balls[0].x,balls[0].y);
    for(var i:uint = 1; i < numBalls; i++) {
        var ballA:Ball = balls[i-1];
        var ballB:Ball = balls[i];
        moveBall(ballB,ballA.x,ballA.y);
        graphics.lineTo(ballB.x,ballB.y);
    }
}

運(yùn)行結(jié)果見圖 8-3,大家可以在ChainArray.as找到這個(gè)類。

圖8-3 彈簧鏈

多目標(biāo)彈性運(yùn)動(dòng)

我們?cè)诘谖逭掠懻撍俣扰c加速度時(shí),曾說過如何使一個(gè)物體受到多種外力。如果每種力都是加速度,我們只需要把它們一個(gè)個(gè)都加到速度向量中去。因?yàn)閺椓Σ贿^就是施加在物體上的一種加速度,因此在一個(gè)物體上添加多種彈力也是非常容易的。

下面是創(chuàng)建多目標(biāo)彈簧的方法:我們需要三個(gè)控制點(diǎn),這些點(diǎn)都是 Ball 類的實(shí)例,并且具有簡(jiǎn)單的拖拽功能,用它們作為小球彈性運(yùn)動(dòng)的控制點(diǎn)。小球會(huì)立即運(yùn)動(dòng)到點(diǎn),并在兩點(diǎn)間尋找平衡。換句話講,每個(gè)目標(biāo)都會(huì)對(duì)小球施加一定的外力,小球的運(yùn)動(dòng)速度就是這些外力相加的結(jié)果。

例子程序相當(dāng)復(fù)雜,使用多個(gè)方法處理不同的事件。以下是代碼(文檔類 MultiSpring.as),看過后再進(jìn)行分段講解:

package {
 import flash.display.Sprite;
 import flash.events.Event;
 import flash.events.MouseEvent;
 public class MultiSpring extends Sprite {
  private var ball:Ball;
  private var handles:Array;
  private var spring:Number = 0.1;
  private var friction:Number = 0.8;
  private var numHandles:Number = 3;
  public function MultiSpring() {
   init();
  }
  private function init():void {
   ball = new Ball(20);
   addChild(ball);
   handles = new Array();
   for (var i:uint = 0; i < numHandles; i++) {
    var handle:Ball = new Ball(10,0x0000ff);
    handle.x = Math.random() * stage.stageWidth;
    handle.y = Math.random() * stage.stageHeight;
    handle.addEventListener(MouseEvent.MOUSE_DOWN,onPress);
    addChild(handle);
    handles.push(handle);
   }
   addEventListener(Event.ENTER_FRAME,onEnterFrame);
   addEventListener(MouseEvent.MOUSE_UP,onRelease);
  }
  private function onEnterFrame(event:Event):void {
   for (var i:uint = 0; i < numHandles; i++) {
    var handle:Ball = handles[i] as Ball;
    var dx:Number = handle.x - ball.x;
    var dy:Number = handle.y - ball.y;
    ball.vx += dx * spring;
    ball.vy += dy * spring;
   }
   ball.vx *= friction;
   ball.vy *= friction;
   ball.x += ball.vx;
   ball.y += ball.vy;
   graphics.clear();
   graphics.lineStyle(1);
   for (i = 0; i < numHandles; i++) {
    graphics.moveTo(ball.x,ball.y);
    graphics.lineTo(handles[i].x,handles[i].y);
   }
  }
  private function onPress(event:MouseEvent):void {
   event.target.startDrag();
  }
  private function onRelease(event:MouseEvent):void {
   stopDrag();
  }
 }
}

在init方法中,創(chuàng)建小球并用for循環(huán)創(chuàng)建三個(gè)控制點(diǎn),隨機(jī)安排位置,并為它們?cè)O(shè)置拖拽行為。

onEnterFrame方法循環(huán)取出每個(gè)控制點(diǎn),使小球向該點(diǎn)方向運(yùn)動(dòng)。然后,用控制點(diǎn)的坐標(biāo)設(shè)置小球的速度,反復(fù)循環(huán),從小球開始向各個(gè)控制點(diǎn)畫線。onPress方法的內(nèi)容非常簡(jiǎn)單,但是請(qǐng)注意 onRelease函數(shù),我們無(wú)法知道當(dāng)前拖拽的是哪個(gè)小球。幸運(yùn)的是,使用任何一個(gè)顯示調(diào)用stopDrag方法,都可以停止所有的拖拽,所以只需要在文檔類中直接調(diào)用該方法。

我們只要改變 numHandles 變量的值,就可以輕松地設(shè)置控制點(diǎn)的數(shù)量。運(yùn)行結(jié)果如圖 8-4所示。

圖8-4 多目標(biāo)彈性

到目前為止,我相信大家已經(jīng)有了很多的心得與體會(huì),并且開始嘗試解決一些書中沒有提到的問題。如果真是這樣的話,那就太好了!這也正是我寫這本書的目的。

目標(biāo)偏移

我們拿到一個(gè)真正的彈簧——有彈性的金屬圈——然后將它的一頭固定起來(lái),另一頭放上小球或其它物體,那么物體運(yùn)動(dòng)的目標(biāo)點(diǎn)是哪里?難道說目標(biāo)點(diǎn)是固定彈簧的那頭兒?不,這并不實(shí)際。小球永遠(yuǎn)也到不了這個(gè)點(diǎn),因?yàn)樗鼤?huì)受到彈簧自身的阻礙。一旦彈簧變回了正常的長(zhǎng)度,它會(huì)對(duì)小球施加更大的力。因此,目標(biāo)點(diǎn)就應(yīng)該是彈簧展開后的末端。

要尋找目標(biāo)點(diǎn),首先要找到物體與固定點(diǎn)之間的夾角,然后沿這個(gè)角度從固定點(diǎn)向外展開一段長(zhǎng)度——彈簧的長(zhǎng)度。換句話講,如果彈簧長(zhǎng)度是 50,小球與固定點(diǎn)的夾角是 45 度的話,那么就要以 45 度的夾角向外運(yùn)動(dòng) 50 個(gè)像素,而這個(gè)點(diǎn)就是小球的目標(biāo)點(diǎn)。圖8-5 解釋了這一過程。

圖8-5 彈簧復(fù)位

尋找目標(biāo)點(diǎn)的代碼如下:

var dx:Number = ball.x - fixedX;
var dy:Number = ball.y - fixedY;
var angle:Number = Math.atan2(dy,dx);
var targetX:Number = fixedX + Math.cos(angle) * springLength;
var targetY:Number = fixedY + Math.sin(angle) * springLength;

運(yùn)行結(jié)果是,物體向著固定點(diǎn)運(yùn)動(dòng),但會(huì)在與目標(biāo)點(diǎn)相差一段距離時(shí)停止移動(dòng)。大家還要注意,雖然我們叫它“固定點(diǎn)”,只是代表彈簧固定到的某個(gè)點(diǎn)。而不是指這個(gè)點(diǎn)不能移動(dòng)。也許最好的方法就是看代碼。

我們繼續(xù)使用鼠標(biāo)位置作為固定點(diǎn),彈簧的長(zhǎng)度為100 像素。以下是文檔類(OffsetSpring.as):

package {
 import flash.display.Sprite;
 import flash.events.Event;
 public class OffsetSpring extends Sprite {
  private var ball:Ball;
  private var spring:Number = 0.1;
  private var vx:Number = 0;
  private var vy:Number = 0;
  private var friction:Number = 0.95;
  private var springLength:Number = 100;
  public function OffsetSpring() {
   init();
  }
  private function init():void {
   ball = new Ball(20);
   addChild(ball);
   addEventListener(Event.ENTER_FRAME,onEnterFrame);
  }
  private function onEnterFrame(event:Event):void {
   var dx:Number = ball.x - mouseX;
   var dy:Number = ball.y - mouseY;
   var angle:Number = Math.atan2(dy,dx);
   var targetX:Number = mouseX + Math.cos(angle) * springLength;
   var targetY:Number = mouseY + Math.sin(angle) * springLength;
   vx += (targetX - ball.x) * spring;
   vy += (targetY - ball.y) * spring;
   vx *= friction;
   vy *= friction;
   ball.x += vx;
   ball.y += vy;
   graphics.clear();
   graphics.lineStyle(1);
   graphics.moveTo(ball.x,ball.y);
   graphics.lineTo(mouseX,mouseY);
  }
 }
}

雖然我們能夠看到運(yùn)行結(jié)果,但卻不能真正發(fā)現(xiàn)這項(xiàng)技術(shù)的特殊用處。沒關(guān)系,下一節(jié)會(huì)給大家一個(gè)特別的例子。

彈簧連接多個(gè)物體

我們知道如何用彈簧連接兩個(gè)物體,還知道這個(gè)點(diǎn)不是固定的。但是,如果另一個(gè)物體上還有一個(gè)彈簧反作用在第一個(gè)物體上,又是怎樣的呢?這里有兩個(gè)物體之間由一根彈簧連接。其中一個(gè)運(yùn)動(dòng)了,另一個(gè)物體就要向該物體移動(dòng)過來(lái)。

我開始認(rèn)為制作這種效果會(huì)導(dǎo)致死循環(huán)從而無(wú)法實(shí)現(xiàn),或者至少會(huì)引起錯(cuò)誤。但我也沒管那么多,勇敢地進(jìn)行嘗試。結(jié)果非常完美!

雖然前面已經(jīng)描述了一些策略,但這里還要細(xì)致得說一下:物體A以物體B作為目標(biāo),并向它移動(dòng)。物體B 反過來(lái)又以物體A作為目標(biāo)。事實(shí)上,本例中目標(biāo)偏移起了重要的作用。如果一個(gè)物體以其它物體直接作為目標(biāo),那么它們之就會(huì)相互吸引,最終聚集在一個(gè)點(diǎn)上。通過使用偏移目標(biāo),我們就可以使它們之間保持距離,如圖 8-6所示。

圖8-6 彈簧連接的兩個(gè)物體

下面舉一個(gè)例子,我們需要兩個(gè) Ball 類的實(shí)例。分別為ball0 和 ball1。ball0向ball1 偏移運(yùn)動(dòng)。ball1向ball0 偏移運(yùn)動(dòng)。為了不去反復(fù)寫偏移彈性運(yùn)動(dòng)的代碼,我們將這些功能寫到函數(shù) springTo 中,直接調(diào)用函數(shù)即可。如果想讓 ball0向ball1 運(yùn)動(dòng),只要寫 springTo(ball0,ball1),然后再讓 ball1向ball0 運(yùn)動(dòng),就寫 springTo(ball1,ball0)。還要設(shè)置兩個(gè)變量,ball0Dragging 和 ball1Dragging,作為每個(gè)小球運(yùn)動(dòng)的開關(guān)。以下是文檔類(DoubleSpring.as):

package {
 import flash.display.Sprite;
 import flash.events.Event;
 import flash.events.MouseEvent;
 public class DoubleSpring extends Sprite {
  private var ball0:Ball;
  private var ball1:Ball;
  private var ball0Dragging:Boolean = false;
  private var ball1Dragging:Boolean = false;
  private var spring:Number = 0.1;
  private var friction:Number = 0.95;
  private var springLength:Number = 100;
  public function DoubleSpring() {
   init();
  }
  private function init():void {
   ball0 = new Ball(20);
   ball0.x = Math.random() * stage.stageWidth;
   ball0.y = Math.random() * stage.stageHeight;
   ball0.addEventListener(MouseEvent.MOUSE_DOWN,onPress);
   addChild(ball0);
   ball1 = new Ball(20);
   ball1.x = Math.random() * stage.stageWidth;
   ball1.y = Math.random() * stage.stageHeight;
   ball1.addEventListener(MouseEvent.MOUSE_DOWN,onPress);
   addChild(ball1);
   addEventListener(Event.ENTER_FRAME,onEnterFrame);
   stage.addEventListener(MouseEvent.MOUSE_UP,onRelease);
  }
  private function onEnterFrame(event:Event):void {
   if (!ball0Dragging) {
    springTo(ball0,ball1);
   }
   if (!ball1Dragging) {
    springTo(ball1,ball0);
   }
   graphics.clear();
   graphics.lineStyle(1);
   graphics.moveTo(ball0.x,ball0.y);
   graphics.lineTo(ball1.x,ball1.y);
  }
  private function springTo(ballA:Ball,ballB:Ball):void {
   var dx:Number = ballB.x - ballA.x;
   var dy:Number = ballB.y - ballA.y;
   var angle:Number = Math.atan2(dy,dx);
   var targetX:Number = ballB.x - Math.cos(angle) * springLength;
   var targetY:Number = ballB.y - Math.sin(angle) * springLength;
   ballA.vx += (targetX - ballA.x) * spring;
   ballA.vy += (targetY - ballA.y) * spring;
   ballA.vx *= friction;
   ballA.vy *= friction;
   ballA.x += ballA.vx;
   ballA.y += ballA.vy;
  }
  private function onPress(event:MouseEvent):void {
   event.target.startDrag();
   if (event.target == ball0) {
    ball0Dragging = true;
   }
   if (event.target == ball1) {
    ball1Dragging = true;
   }
  }
  private function onRelease(event:MouseEvent):void {
   ball0.stopDrag();
   ball1.stopDrag();
   ball0Dragging = false;
   ball1Dragging = false;
  }
 }
}

本例中,每個(gè)小球都是可以拖拽的。enterFrame函數(shù)負(fù)責(zé)為小球調(diào)用springTo函數(shù)。請(qǐng)注意,這兩條語(yǔ)句都是由兩條判斷語(yǔ)句包圍起來(lái)的,目的是要確認(rèn)小球目前沒被拖拽:

springTo(ball0,ball1);
springTo(ball1,ball0);

springTo函數(shù)用于產(chǎn)生運(yùn)動(dòng),函數(shù)中的所有語(yǔ)句大家應(yīng)該都很熟悉。首先,求出距離和角度,再計(jì)算目標(biāo)點(diǎn),然后向目標(biāo)點(diǎn)運(yùn)動(dòng)。第二次調(diào)用函數(shù)時(shí),參數(shù)相反,兩個(gè)小球交換位置,開始的小球向另一個(gè)小球運(yùn)動(dòng)。這也許不是效率最高的代碼,但是它可以最好地表現(xiàn)出運(yùn)動(dòng)的過程。

我們看到,小球不會(huì)依附在任何固定點(diǎn)上,它們都是自由飄浮的。小球之間唯一的約束就是彼此保持一定的距離。這種寫法最好的地方是可以很容易地加入新的物體。例如,再創(chuàng)建第三個(gè)小球(ball2),同時(shí)為它設(shè)置一個(gè)變量(ball2Dragging),就可以這么添加:

if(!ball0Dragging) {
    springTo(ball0,ball1);
    springTo(ball0,ball2);
}
if(!ball1Dragging) {
    springTo(ball1,ball0);
    springTo(ball1,ball2);
}
if(!ball2Dragging) {
    springTo(ball2,ball0);
    springTo(ball2,ball1);
}

這樣就建立了一個(gè)三角形結(jié)構(gòu),如圖 8-7所示。大家熟練掌握后,很快就能做出四邊形結(jié)構(gòu),直到一切復(fù)雜的彈簧結(jié)構(gòu)。

圖8-7 一根彈簧連接三個(gè)物體

本章重要公式總結(jié)

現(xiàn)在來(lái)回顧一下本章的重要公式

簡(jiǎn)單緩動(dòng),長(zhǎng)形:

var dx:Number = targetX - sprite.x;
var dy:Number = targetY - sprite.y;
vx = dx * easing;
vy = dy * easing;
sprite.x += vx;
sprite.y += vy;

簡(jiǎn)單緩動(dòng),中形:

vx = (targetX - sprite.x) * easing;
vy = (targetY - sprite.y) * easing;
sprite.x += vx;
sprite.y += vy;

簡(jiǎn)單緩動(dòng),短形:

sprite.x += (targetX - sprite.x) * easing;
sprite.y += (targetY - sprite.y) * easing;

簡(jiǎn)單彈性,長(zhǎng)形:

var ax:Number = (targetX - sprite.x) * spring;
var ay:Number = (targetY - sprite.y) * spring;
vx += ax;
vy += ay;
vx *= friction;
vy *= friction;
sprite.x += vx;
sprite.y += vy;

簡(jiǎn)單彈性,中形:

vx += (targetX - sprite.x) * spring;
vy += (targetY - sprite.y) * spring;
vx *= friction;
vy *= friction;
sprite.x += vx;
sprite.y += vy;

簡(jiǎn)單彈性,短形:

vx += (targetX - sprite.x) * spring;
vy += (targetY - sprite.y) * spring;
sprite.x += (vx *= friction);
sprite.y += (vy *= friction);

偏移彈性運(yùn)動(dòng):

var dx:Number = sprite.x - fixedX;
var dy:Number = sprite.y - fixedY;
var angle:Number = Math.atan2(dy,dx);
var targetX:Number = fixedX + Math.cos(angle) * springLength;
var targetY:Number = fixedX + Math.sin(angle) * springLength;

聲明:該文章系網(wǎng)友上傳分享,此內(nèi)容僅代表網(wǎng)友個(gè)人經(jīng)驗(yàn)或觀點(diǎn),不代表本網(wǎng)站立場(chǎng)和觀點(diǎn);若未進(jìn)行原創(chuàng)聲明,則表明該文章系轉(zhuǎn)載自互聯(lián)網(wǎng);若該文章內(nèi)容涉嫌侵權(quán),請(qǐng)及時(shí)向上學(xué)吧網(wǎng)站投訴>>
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
as3煙花
動(dòng)量守恒與能量守恒
180行JavaScript代碼實(shí)現(xiàn)的小球隨機(jī)移動(dòng)代碼
2D WebGL renderer Pixi.js v4 入門【最終回】
Canvas2D mobile web game development – implementation | Tizen Developers
Flash AS3.0制作飄動(dòng)的氣泡內(nèi)含詳細(xì)注釋
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服