ios中实现staggeringBeauty的效果(三)
我们直接进入主题吧。
先由main.js来入手。https://github.com/georgealways/staggeringbeauty/blob/master/src/main.js
这里我只展开需要的部分,其他部分先忽略,一开始声明了很多的变量,其中重要的有pathBody会调用
function buildBody() {
for (i = 0; i < numPoints; i++) {
var x = view.size.width / 2;
var y = view.size.height - (i-1)*segmentLength;
var particle = physics.makeParticle(2.5, x, y, 0);
var support = physics.makeParticle(1, x, y - segmentLength, 0);
var support2 = physics.makeParticle(1, x, y + segmentLength, 0);
if (i > 0) {
var prevSupport = supports[i-1];
var prevParticle = particles[i-1];
physics.makeSpring(particle, prevSupport, 0.6, 0.48, 0);
physics.makeSpring(prevParticle, support2, 0.3, 0.7, 0);
springs.push(physics.makeSpring(particle, prevParticle, 0.2, 0.1, segmentLength));
}
if (i < 2) {
particle.makeFixed();
}
support.makeFixed();
support2.makeFixed();
var point = new Point();
pathBody.add(point);
// pathBodyStroke.add(point);
// pathShadow.add(point);
particles.push(particle);
supports.push(support);
supports2.push(support2);
}
}
来建立body,这里想要说的是supports与supports2的用来不断去设限制条件(力和距离的控制点在oc中要重点实现)。
function setPositions() {
var e = mouseDown ? params.mouseTargetEasing : params.mouseTargetEasingUp;
mousePos.x += (targetMousePos.x - mousePos.x) * e;
mousePos.y += (targetMousePos.y - mousePos.y) * e;
particles[1].position.x = mousePos.x;
particles[1].position.y = mousePos.y;
var targetStress = 0;
for (var i = 1; i < numPoints; i++) {
var support = supports[i];
var curParticle = particles[i];
curParticle.position.x = clamp(curParticle.position.x, padding, view.size.width-padding);
var prevParticle = particles[i-1];
var angle = Math.atan2(curParticle.position.y - prevParticle.position.y, curParticle.position.x - prevParticle.position.x);
var force = curParticle.force.length();
if (force > maxForce) {
curParticle.force.scale(force / maxForce);
force = maxForce;
}
if (i > 1) targetStress += force;
support.position.x = curParticle.position.x + Math.cos(angle + i*bend)*segmentLength;
support.position.y = curParticle.position.y + Math.sin(angle + i*bend)*segmentLength;
pathBody.segments[i].angle = angle;
var support2 = supports2[i];
support2.position.x = curParticle.position.x + Math.cos(Math.PI + angle)*segmentLength;
support2.position.y = curParticle.position.y + Math.sin(Math.PI + angle)*segmentLength;
}
stress += (targetStress-stress)*0.03;
}
function updateAppearance() {
if (stress > 80) {
hue += stress > 300 ? Math.pow(stress/5, 2) : stress;
pathBody.strokeColor = 'hsl('+Math.round(hue)%360+', 100%, 50%)';
pathMouth.strokeColor = 'hsl('+Math.round(hue)%360+', 100%, 90%)';
pathTongue.strokeColor = 'hsl('+Math.round(hue)%360+', 100%, 50%)';
pathMouth.strokeWidth = thickness/1.5 + (Math.random()-Math.random())*20;
pathTongue.strokeWidth = pathMouth.strokeWidth*0.65;
// pathShadow.strokeColor = '#000';
peaking = true;
clearTimeout(wrong);
document.getElementById('shake').style.display = 'none';
} else {
pathBody.strokeColor = params.fillColor;
// pathBodyStroke.strokeColor = params.strokeColor;
// pathShadow.strokeColor = null;
peaking = false;
}
if (peaking) {
// document.body.className = Math.random() < 0.8 ? 'bg-a' : 'bg-b';
document.body.className = frameCount % 3 != 0 ? 'bg-a' : 'bg-b';
if (!prevPeaking) {
physics.drag = 0.2;
pathMouth.opacity = 1;
pathTongue.opacity = 1;
// unSmooth();
eyeJiggler.rest = 17;
}
if (firstPlay && audio) {
// audio.play({ loops: 1000 });
// console.log('set volume');
audio.setVolume(50);
audio2.setVolume(70);
firstPlay = false;
}
} else if (!peaking && prevPeaking) {
document.body.className = '';
physics.drag = restDrag;
pathMouth.opacity = 0;
pathTongue.opacity = 0;
eyeJiggler.rest = 6;
if (audio) {
// audio.pause();
audio.setVolume(0);
audio2.setVolume(0);
firstPlay = true;
}
}
prevPeaking = peaking;
}
function updatePaths() {
for (var i = 0, j, l; i < numPoints; i++) {
var curParticle = particles[i];
var prevParticle = particles[i-1];
var angle = pathBody.segments[i].angle + Math.PI/2;
pathBody.segments[i].point.x = curParticle.position.x;
pathBody.segments[i].point.y = curParticle.position.y;
// pathBodyStroke.segments[i].point.x = curParticle.position.x;
// pathBodyStroke.segments[i].point.y = curParticle.position.y;
j = i-mouthPadding+1;
l = pathMouth.segments.length;
if (j >= 0 && j < l) {
pathMouth.segments[j].point.x = curParticle.position.x;// + (Math.random() - Math.random())*10;
pathMouth.segments[j].point.y = curParticle.position.y;// + (Math.random() - Math.random())*10;
pathMouth.segments[j].point.x += Math.cos(angle)*thickness/6;
pathMouth.segments[j].point.y += Math.sin(angle)*thickness/10;
if (pathTongue.segments[j]) {
pathTongue.segments[j].point.x = pathMouth.segments[j].point.x;
pathTongue.segments[j].point.y = pathMouth.segments[j].point.y;
pathTongue.segments[j].point.x -= Math.cos(angle)*(pathMouth.strokeWidth/15);
pathTongue.segments[j].point.y -= Math.sin(angle)*(pathMouth.strokeWidth/5);
}
}
// pathShadow.segments[i].point.x = curParticle.position.x+50;
// pathShadow.segments[i].point.y = curParticle.position.y-10;
}
if (smooth) {
for (var i = 0, l = paths.length; i < l; i++) {
paths[i].smooth();
}
}
}
而这里的setPositions() ,updateAppearance(),updatePaths()由它的名字就可以得知它门各自的作用分别是设置点的位置,更新显示,更新paths。这就是我们在oc要重构的东西。
如果只是要实现的话你可以自己,在你工程文件中加入https://github.com/chrismiles/DynamicXray
工程中的Spring Rope文件
然后直接在里面修改,当然这是最直接方法,如果你想一步步的展开,明天的博客,我会按照我当时摸索的一步步来。
这里使用了,外国的这位大神已经为我门做了particle和path了。
我们在layer中实现这些运动。
这里我们先简单说一下CALayer这里就引用大神的话来解释吧,http://www.cnblogs.com/kenshincui/p/3972100.html
去这里看看,试一试我大家就会有所了解,这里姑且把它当作一个需要渲染的图层吧,(后面我还在这图层上加了UIView为了加物理碰撞。。。好象有点前后矛盾在CALayer中加UIView好像并不符合一般的规则)
/*
Physics Configuration
*/
static CGFloat const CMSpringyRopeDamping = 1.0f;
static CGFloat const CMSpringyRopeFrequency = 4.0f;
static CGFloat const CMSpringyRopeParticleDensity = 1.0f;
static CGFloat const CMSpringyRopeParticleResistance = 1.0f;
/*
Visual Configuration
*/
static CGFloat const CMSpringyRopeLength =300.0f;
static NSUInteger const CMSpringyRopeSubdivisons = 8;
static CGFloat const CMSpringyRopeHandleRadius = 5.0f;
static CGFloat const stressNumber=1300;
/*
Utility Functions
*/
static CGFloat CGPointDistance(CGPoint userPosition, CGPoint prevPosition)
{
CGFloat dx = prevPosition.x - userPosition.x;
CGFloat dy = prevPosition.y - userPosition.y;
return sqrtf(dx*dx + dy*dy);
}
/*
CMSpringyRopeLayer
*/
@interface CMSpringyRopeLayer ()
@property (assign, nonatomic) float rope_length;
@property (assign, nonatomic) NSUInteger subdivisions;
@property (assign, nonatomic) BOOL isDragging;
@property (assign, nonatomic) CGSize lastSize;
@property (assign, nonatomic) float gravityScale;
@property (strong, nonatomic) CMMotionManager *motionManager;
// Physics
@property (strong, nonatomic) UIDynamicAnimator *animator;
@property (strong, nonatomic) UIGravityBehavior *gravityBehavior;
@property (strong, nonatomic) UICollisionBehavior *collisionBehavior;
@property (strong, nonatomic) NSArray *particles;
@property (assign,nonatomic) CGFloat addGravite;
//support
@property (strong,nonatomic)NSArray *supports1;
@property (strong,nonatomic)NSArray *supports2;
@property (strong, nonatomic) UIAttachmentBehavior *anchorSpringBehavior;
@property (strong, nonatomic) CMSpringyRopeParticle *handleParticle;
@property (strong, nonatomic) UIAttachmentBehavior *handleSpringBehavior;
@property (strong, nonatomic) UIDynamicItemBehavior *particleBehavior;
/*
Physics Configuration
*/
static CGFloat const CMSpringyRopeDamping = 1.0f;
static CGFloat const CMSpringyRopeFrequency = 4.0f;
static CGFloat const CMSpringyRopeParticleDensity = 1.0f;
static CGFloat const CMSpringyRopeParticleResistance = 1.0f;
/*
Visual Configuration
*/
static CGFloat const CMSpringyRopeLength =300.0f;
static NSUInteger const CMSpringyRopeSubdivisons = 8;
static CGFloat const CMSpringyRopeHandleRadius = 5.0f;
static CGFloat const stressNumber=1300;
/*
Utility Functions
*/
static CGFloat CGPointDistance(CGPoint userPosition, CGPoint prevPosition)
{
CGFloat dx = prevPosition.x - userPosition.x;
CGFloat dy = prevPosition.y - userPosition.y;
return sqrtf(dx*dx + dy*dy);
}
/*
CMSpringyRopeLayer
*/
@interface CMSpringyRopeLayer ()
@property (assign, nonatomic) float rope_length;
@property (assign, nonatomic) NSUInteger subdivisions;
@property (assign, nonatomic) BOOL isDragging;
@property (assign, nonatomic) CGSize lastSize;
@property (assign, nonatomic) float gravityScale;
@property (strong, nonatomic) CMMotionManager *motionManager;
// Physics
@property (strong, nonatomic) UIDynamicAnimator *animator;
@property (strong, nonatomic) UIGravityBehavior *gravityBehavior;
@property (strong, nonatomic) UICollisionBehavior *collisionBehavior;
@property (strong, nonatomic) NSArray *particles;
@property (assign,nonatomic) CGFloat addGravite;
//support
@property (strong,nonatomic)NSArray *supports1;
@property (strong,nonatomic)NSArray *supports2;
@property (strong, nonatomic) UIAttachmentBehavior *anchorSpringBehavior;
@property (strong, nonatomic) CMSpringyRopeParticle *handleParticle;
@property (strong, nonatomic) UIAttachmentBehavior *handleSpringBehavior;
@property (strong, nonatomic) UIDynamicItemBehavior *particleBehavior;
@property (strong, nonatomic) DynamicXray *dynamicXray;
// FPS
@property (assign, nonatomic) double fps_prev_time;
@property (assign, nonatomic) NSUInteger fps_count;
@property (assign,nonatomic) int eyesNumber;
@property(assign,nonatomic) CGFloat stress;
@end
@implementation CMSpringyRopeLayer
@property (strong, nonatomic) DynamicXray *dynamicXray;
// FPS
@property (assign, nonatomic) double fps_prev_time;
@property (assign, nonatomic) NSUInteger fps_count;
@property (assign,nonatomic) int eyesNumber;
@property(assign,nonatomic) CGFloat stress;
@end
@implementation CMSpringyRopeLayer
先定义了一大部分的属性,变量的大家就看着英文去了解吧。。我就不逐个去翻译了。其中的DynamicXray *dynamicXray 是这个应用的一个功能,用Xray去观察运动,这个用来去慢慢调整那个魔性的晃动效果简直不能太赞了。(感谢神让我找到他。。)
先补充一句上面的代码是我已经做了修改的了,所以和Github上的有的不一样,在最后好会把我的做成游戏的代码附上,因为把它提交了之后,就赶着做第二款游戏,都没有整理一下代码。。。乱的够可以的,所以我整理之后会在最后的一篇中把代码附上。
好吧继续分析代码,轮到init了
- (id)init
{
self = [super init];
if (self) {
//适应屏幕大小
self.contentsScale = [UIScreen mainScreen].scale;
_lastSize = self.bounds.size;
_rope_length = CMSpringyRopeLength;
_subdivisions = CMSpringyRopeSubdivisons;
_motionManager = [[CMMotionManager alloc] init];
_motionManager.deviceMotionUpdateInterval = 0.02; // 50 Hz
//add UIDynamicAnimator
_animator = [[UIDynamicAnimator alloc] init];
//add UIGravityBehavior
_gravityBehavior = [[UIGravityBehavior alloc] initWithItems:nil];
_gravityBehavior.gravityDirection = CGVectorMake(0.0, -0.5f);
__weak CMSpringyRopeLayer *weakSelf = self;
_gravityBehavior.action = ^{
__strong CMSpringyRopeLayer *strongSelf = weakSelf;
[strongSelf drawFrame];
};
[_animator addBehavior:_gravityBehavior];
_particleBehavior = [[UIDynamicItemBehavior alloc] initWithItems:nil];
_particleBehavior.density = CMSpringyRopeParticleDensity;
_particleBehavior.resistance = CMSpringyRopeParticleResistance;
[_animator addBehavior:_particleBehavior];
self.dynamicXray = [[DynamicXray alloc] init];
[self.animator addBehavior:self.dynamicXray];
}
return self;
}
主要是加物理系统,这里用到了苹果自带的物理系统而没有使用traer.js,用了traer.js的重构的那个后面我会在提到。但这里也会跟据traer.js的原理来使用苹果自带的物理系统 UIDynamicAnimator
可以借鉴http://my.oschina.net/u/1378445/blog/335014
然后就是实现main.js中的particle了
#pragma mark - Set Up Physics
- (void)generateParticles
{
NSMutableArray *particles = [NSMutableArray array];
NSMutableArray *supports1=[NSMutableArray array];
NSMutableArray *supports2=[NSMutableArray array];
CGPoint anchorPoint = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetHeight(self.bounds));
NSUInteger subdivisions = self.subdivisions;
float sub_len = self.rope_length / subdivisions;
for (NSUInteger i=1; i<=subdivisions; i++) {
UIView *p = [[UIView alloc] initWithFrame:CGRectMake(anchorPoint.x, anchorPoint.y - i*sub_len, 10, 10)];
[particles addObject:p];
CMSpringyRopeParticle *support1=[[CMSpringyRopeParticle alloc]initWithCenterPosition:CGPointMake(anchorPoint.x, anchorPoint.y - (i-1)*sub_len)];
[supports1 addObject:support1];
CMSpringyRopeParticle *support2=[[CMSpringyRopeParticle alloc]initWithCenterPosition:CGPointMake(anchorPoint.x, anchorPoint.y - (i+1)*sub_len)];
[supports2 addObject:support2];
[self.particleBehavior addItem:p];
[self.gravityBehavior addItem:p];
[self.particleBehavior addItem:support1];
[self.particleBehavior addItem:support2];
}
self.handleParticle = [particles objectAtIndex:1];
NSUInteger particlesMaxIndex = [particles count] - 1;
//add SpringBehavior
for (NSUInteger i=0; i<particlesMaxIndex; i++) {
if (i == 0) {
self.anchorSpringBehavior = [self addSpringBehaviorWithItem:particles[i] attachedToAnchor:anchorPoint];
}
UIAttachmentBehavior *springBehavior = [self addSpringBehaviorWithItem:particles[i] attachedToItem:particles[i+1]];
UIAttachmentBehavior *support1SpringBehavior=[self addSuppotr1SpringBehaviorWithItem:supports1[i+1] attachedToItem:particles[i]];
UIAttachmentBehavior *support2SpringBehavior=[self addSuppotr2SpringBehaviorWithItem: supports2[i]attachedToItem:particles[i+1]];
if (i == 1) {
self.handleSpringBehavior = springBehavior;
}
}
// UIView * leftconner=[[UIView alloc] initWithFrame:CGRectMake(CGRectGetMinX(self.bounds), CGRectGetMinY(self.bounds), 20, 20)];
//
// UIView * rightconner=[[UIView alloc] initWithFrame:CGRectMake(CGRectGetMaxX(self.bounds)-10, CGRectGetMinY(self.bounds), 10, CGRectGetMaxY(self.bounds))];
self.particles = particles;
self.supports1=supports1;
self.supports2=supports2;
// NSMutableArray * collisonView=[NSMutableArray arrayWithArray:particles];
// [collisonView addObject:leftconner];
// [collisonView addObject:rightconner];
UICollisionBehavior *collisionBehavior = [[UICollisionBehavior alloc] initWithItems:particles];
collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
collisionBehavior.collisionMode=UICollisionBehaviorModeEverything;
[collisionBehavior addBoundaryWithIdentifier:@"line1" fromPoint:CGPointMake(CGRectGetMinX(self.bounds)+10, CGRectGetMinY(self.bounds)) toPoint:CGPointMake(CGRectGetMinX(self.bounds)+10,CGRectGetMaxY(self.bounds)+400)];
[collisionBehavior addBoundaryWithIdentifier:@"line2" fromPoint:CGPointMake(CGRectGetMaxX(self.bounds)-10, CGRectGetMinY(self.bounds)) toPoint:CGPointMake(CGRectGetMaxX(self.bounds)-10,CGRectGetMaxY(self.bounds)+400)];
// UICollisionBehavior *collisionBehavior2 = [[UICollisionBehavior alloc] initWithItems:@[leftconner,rightconner]];
// collisionBehavior2.translatesReferenceBoundsIntoBoundary = YES;
// collisionBehavior2.collisionMode=UICollisionBehaviorModeEverything;
//
[self.animator addBehavior:collisionBehavior];
//[self.animator addBehavior:collisionBehavior2];
}
按照traer.js在particle间加了Spring弹簧引力。
其中各个particle,support1,support2间力的作用由下面的函数来实现
- (UIAttachmentBehavior *)addSpringBehaviorWithItem:(id<UIDynamicItem>)item attachedToAnchor:(CGPoint)anchorPoint
{
UIAttachmentBehavior *springBehavior = [[UIAttachmentBehavior alloc] initWithItem:item attachedToAnchor:anchorPoint];
[self configureSpringBehavior:springBehavior];
[self.animator addBehavior:springBehavior];
return springBehavior;
}
- (UIAttachmentBehavior *)addSpringBehaviorWithItem:(id<UIDynamicItem>)itemA attachedToItem:(id<UIDynamicItem>)itemB
{
UIAttachmentBehavior *springBehavior = [[UIAttachmentBehavior alloc] initWithItem:itemA attachedToItem:itemB];
[self configureSpringBehavior:springBehavior];
[self.animator addBehavior:springBehavior];
return springBehavior;
}
- (UIAttachmentBehavior *)addSuppotr1SpringBehaviorWithItem:(id<UIDynamicItem>)itemA attachedToItem:(id<UIDynamicItem>)itemB
{
UIAttachmentBehavior *springBehavior = [[UIAttachmentBehavior alloc] initWithItem:itemA attachedToItem:itemB];
[self configureSupport1SpringBehavior:springBehavior];
[self.animator addBehavior:springBehavior];
return springBehavior;
}
- (UIAttachmentBehavior *)addSuppotr2SpringBehaviorWithItem:(id<UIDynamicItem>)itemA attachedToItem:(id<UIDynamicItem>)itemB
{
UIAttachmentBehavior *springBehavior = [[UIAttachmentBehavior alloc] initWithItem:itemA attachedToItem:itemB];
[self configureSupport2SpringBehavior:springBehavior];
[self.animator addBehavior:springBehavior];
return springBehavior;
}
- (void)configureSpringBehavior:(UIAttachmentBehavior *)springBehavior
{
float sub_len = self.rope_length / self.subdivisions;
springBehavior.length = sub_len;
springBehavior.frequency = CMSpringyRopeFrequency;
springBehavior.damping = CMSpringyRopeDamping;
}
- (void)configureSupport1SpringBehavior:(UIAttachmentBehavior *)springBehavior
{
// float sub_len = self.rope_length / self.subdivisions;
springBehavior.length = 0.0;
springBehavior.frequency = CMSpringyRopeFrequency*7;
springBehavior.damping = CMSpringyRopeDamping*2;
}
- (void)configureSupport2SpringBehavior:(UIAttachmentBehavior *)springBehavior
{
//float sub_len = self.rope_length / self.subdivisions;
springBehavior.length = 0.0;
springBehavior.frequency = CMSpringyRopeFrequency*3;
springBehavior.damping = CMSpringyRopeDamping*5;
}
这是按照traer.js然后作细调的来实现的。
然后有update,与玩家的交互什么的明天在分析,今天代码量有点多一天不能吃成胖子嘛,明天ios中实现staggeringBeauty的效果(四)继续,尽量不跳票。。。。