/// <summary> /// Make the camera follow an object with a specified follow-style /// </summary> /// <param name="Target"></param> /// <param name="Style"></param> public void follow(FlxObject Target, uint Style = STYLE_LOCKON) { target = Target; float helper, w, h; switch (Style) { case STYLE_PLATFORMER: w = width / 8; h = height / 3; deadzone = new FlxRect((width - w) / 2, (height - h) / 2 - h * 0.25f, w, h); break; case STYLE_TOPDOWN: helper = FlxU.max(width, height) / 3; deadzone = new FlxRect((width - helper) / 3, (height - helper) / 3, helper, helper); break; case STYLE_TOPDOWN_TIGHT: helper = FlxU.max(width, height) / 12; deadzone = new FlxRect((width - helper) / 2, (height - helper) / 2, helper, helper); break; case STYLE_LOCKON: break; case STYLE_LOOSE: deadzone = new FlxRect(0, 0, width, height); break; default: deadzone = null; break; } }
/// <summary> /// Internal function used by FlxEmitter to create the particles /// </summary> /// <param name="Graphic"></param> /// <param name="Rotation"></param> /// <param name="Width"></param> /// <param name="Height"></param> /// <returns></returns> public FlxSprite loadParticleGraphic(Texture2D Graphic, float Rotation, float Width = 0, float Height = 0) { rotateBy = Rotation * 0.0174532925f; texture = Graphic; int numFrames = (int)FlxU.floor(texture.Width / texture.Height); if (Width == 0 || Height == 0) { Width = texture.Height; Height = Width; } frameWidth = width = Width; frameHeight = height = Height; List <FlxRect> _draws = new List <FlxRect>(); for (int i = 0; i < numFrames; i++) { FlxRect textureArea = new FlxRect(i * height, 0, width, height); _draws.Add(textureArea); } int di;// = (int)Math.Round(FlxU.randomBetween(0, numFrames)); Random r = new Random(); di = r.Next(numFrames); particleRect = _draws[di]; //FlxRect t = _draws[di]; sourceRect = new FlxRect(particleRect.x, particleRect.y, particleRect.width, particleRect.height); return(this); }
/** * Call this function to give this object a path to follow. * If the path does not have at least one node in it, this function * will log a warning message and return. * * @param Path The <code>FlxPath</code> you want this object to follow. * @param Speed How fast to travel along the path in pixels per second. * @param Mode Optional, controls the behavior of the object following the path using the path behavior constants. Can use multiple flags at once, for example PATH_YOYO|PATH_HORIZONTAL_ONLY will make an object move back and forth along the X axis of the path only. * @param AutoRotate Automatically point the object toward the next node. Assumes the graphic is pointing upward. Default behavior is false, or no automatic rotation. */ public void followPath(FlxPath Path, float Speed = 100, uint Mode = PATH_FORWARD, bool AutoRotate = false) { if (Path.nodes.Count <= 0) { FlxG.log("WARNING: Paths need at least one node in them to be followed."); return; } path = Path; pathSpeed = FlxU.abs(Speed); _pathMode = Mode; _pathRotate = AutoRotate; //get starting node if ((_pathMode == PATH_BACKWARD) || (_pathMode == PATH_LOOP_BACKWARD)) { _pathNodeIndex = path.nodes.Count - 1; _pathInc = -1; } else { _pathNodeIndex = 0; _pathInc = 1; } }
/// <summary> /// Internal function for updating the position and speed of this object. /// </summary> private void updateMotion() { float delta; float velocityDelta; velocityDelta = (FlxU.computeVelocity(angularVelocity, angularAcceleration, angularDrag, maxAngular) - angularVelocity) / 2; angularVelocity += velocityDelta; angle += angularVelocity * FlxG.elapsed; angularVelocity += velocityDelta; velocityDelta = (FlxU.computeVelocity(velocity.x, acceleration.x, drag.x, maxVelocity.x) - velocity.x) / 2; velocity.x += velocityDelta; delta = velocity.x * FlxG.elapsed; velocity.x += velocityDelta; x += delta; velocityDelta = (FlxU.computeVelocity(velocity.y, acceleration.y, drag.y, maxVelocity.y) - velocity.y) / 2; velocity.y += velocityDelta; delta = velocity.y * FlxG.elapsed; velocity.y += velocityDelta; y += delta; }
private void pathLogic() { _currPos.make(x, y); if (moving) { makePathTimer += FlxG.elapsed; } if (makePathTimer >= 0.3) { if (FlxU.getDistance(_currPos, _lastPos) >= 5) { //FlxG.log(FlxU.getDistance(_currPos, _lastPos)); _lastPos.make(x, y); _nodes.Add(_lastPos); if (!_indoors) { footsteps.emitParticle(); } //FlxG.log("+node at ("+_currPos.x+", "+_currPos.y+")"); } makePathTimer = 0; } }
/** * Shoots a ray from the start point to the end point. * If/when it passes through a tile, it stores that point and returns false. * * @param Start The world coordinates of the start of the ray. * @param End The world coordinates of the end of the ray. * @param Result A <code>Point</code> object containing the first wall impact. * @param Resolution Defaults to 1, meaning check every tile or so. Higher means more checks! * @return Returns true if the ray made it from Start to End without hitting anything. Returns false and fills Result if a tile was hit. */ public bool ray(FlxPoint Start, FlxPoint End, FlxPoint Result = null, float Resolution = 1f) { float step = _tileWidth; if (_tileHeight < _tileWidth) { step = _tileHeight; } step /= Resolution; float deltaX = End.x - Start.x; float deltaY = End.y - Start.y; float distance = (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY); int steps = (int)FlxU.ceil(distance / step); float stepX = deltaX / steps; float stepY = deltaY / steps; float curX = Start.x - stepX - x; float curY = Start.y - stepY - y; int tileX; int tileY; int i = 0; while (i < steps) { curX += stepX; curY += stepY; if ((curX < 0) || (curX > width) || (curY < 0) || (curY > height)) { i++; continue; } tileX = (int)curX / _tileWidth; tileY = (int)curY / _tileHeight; if (Convert.ToBoolean((_tileObjects[_data[tileY * widthInTiles + tileX]] as FlxTile).allowCollisions)) { //Some basic helper stuff tileX *= _tileWidth; tileY *= _tileHeight; float rx = 0; float ry = 0; float q; float lx = curX - stepX; float ly = curY - stepY; //Figure out if it crosses the X boundary q = tileX; if (deltaX < 0) { q += _tileWidth; } rx = q; ry = ly + stepY * ((q - lx) / stepX); if ((ry > tileY) && (ry < tileY + _tileHeight)) { if (Result == null) { Result = new FlxPoint(); } Result.x = rx; Result.y = ry; return(false); } //Else, figure out if it crosses the Y boundary q = tileY; if (deltaY < 0) { q += _tileHeight; } rx = lx + stepX * ((q - ly) / stepY); ry = q; if ((rx > tileX) && (rx < tileX + _tileWidth)) { if (Result == null) { Result = new FlxPoint(); } Result.x = rx; Result.y = ry; return(false); } return(true); } i++; } return(true); }
// overlapsWithCallBack public Boolean overlapsWithCallback(FlxObject Object, Func <FlxObject, FlxObject, Boolean> Callback = null, Boolean FlipCallbackParams = false, FlxPoint Position = null) { Boolean results = false; float X = x; float Y = y; if (Position != null) { X = Position.x; Y = Position.x; } //Figure out what tiles we need to check against int selectionX = (int)FlxU.floor((Object.x - X) / _tileWidth); int selectionY = (int)FlxU.floor((Object.y - Y) / _tileHeight); uint selectionWidth = (uint)(selectionX + (FlxU.ceil((int)Object.width / _tileWidth)) + 2); uint selectionHeight = (uint)(selectionY + (FlxU.ceil((int)Object.height / _tileHeight)) + 2); //Then bound these coordinates by the map edges if (selectionX < 0) { selectionX = 0; } if (selectionY < 0) { selectionY = 0; } if (selectionWidth > widthInTiles) { selectionWidth = (uint)widthInTiles; } if (selectionHeight > heightInTiles) { selectionHeight = (uint)heightInTiles; } //Then loop through this selection of tiles and call FlxObject.separate() accordingly uint rowStart = (uint)selectionY * (uint)widthInTiles; uint row = (uint)selectionY; uint column; FlxTile tile; Boolean overlapFound; float deltaX = X - last.x; float deltaY = Y - last.y; while (row < selectionHeight) { column = (uint)selectionX; while (column < selectionWidth) { overlapFound = false; tile = _tileObjects[(int)_data[(int)(rowStart + column)]] as FlxTile; if (Convert.ToBoolean(tile.allowCollisions)) { tile.x = X + (int)column * _tileWidth; tile.y = Y + (int)row * _tileHeight; tile.last.x = tile.x - deltaX; tile.last.y = tile.y - deltaY; if (Callback != null) { if (FlipCallbackParams) { overlapFound = Callback(Object, tile); } else { overlapFound = Callback(tile, Object); } } else { overlapFound = (Object.x + Object.width > tile.x) && (Object.x < tile.x + tile.width) && (Object.y + Object.height > tile.y) && (Object.y < tile.y + tile.height); } if (overlapFound) { if ((tile.callback != null)) { tile.mapIndex = (uint)rowStart + column; tile.callback(tile, Object); } results = true; } } else if ((tile.callback != null)) { tile.mapIndex = (uint)rowStart + (uint)column; tile.callback(tile, Object); } column++; } rowStart += (uint)widthInTiles; row++; } return(results); }
/** * Internal function for moving the object along the path. * Generally this function is called automatically by <code>preUpdate()</code>. * The first half of the function decides if the object can advance to the next node in the path, * while the second half handles actually picking a velocity toward the next node. */ protected void updatePathMotion() { //first check if we need to be pointing at the next node yet _point.x = x + width * 0.5f; _point.y = y + height * 0.5f; FlxPoint node = path.nodes[_pathNodeIndex]; float deltaX = node.x - _point.x; float deltaY = node.y - _point.y; bool horizontalOnly = (_pathMode & PATH_HORIZONTAL_ONLY) > 0; bool verticalOnly = (_pathMode & PATH_VERTICAL_ONLY) > 0; if (horizontalOnly) { if (((deltaX > 0)?deltaX:-deltaX) < pathSpeed * FlxG.elapsed) { node = advancePath(); } } else if (verticalOnly) { if (((deltaY > 0)?deltaY:-deltaY) < pathSpeed * FlxG.elapsed) { node = advancePath(); } } else { if (Math.Sqrt(deltaX * deltaX + deltaY * deltaY) < pathSpeed * FlxG.elapsed) { node = advancePath(); } } //then just move toward the current node at the requested speed if (pathSpeed != 0) { //set velocity based on path mode _point.x = x + width * 0.5f; _point.y = y + height * 0.5f; if (horizontalOnly || (_point.y == node.y)) { velocity.x = (_point.x < node.x)?pathSpeed:-pathSpeed; if (velocity.x < 0) { pathAngle = -90; } else { pathAngle = 90; } if (!horizontalOnly) { velocity.y = 0; } } else if (verticalOnly || (_point.x == node.x)) { velocity.y = (_point.y < node.y)?pathSpeed:-pathSpeed; if (velocity.y < 0) { pathAngle = 0; } else { pathAngle = 180; } if (!verticalOnly) { velocity.x = 0; } } else { pathAngle = FlxU.getAngle(_point, node); FlxU.rotatePoint(0, pathSpeed, 0, 0, pathAngle, velocity); } //then set object rotation if necessary if (_pathRotate) { angularVelocity = 0; angularAcceleration = 0; angle = pathAngle; } } }
// update camera scroll in here // make sure it stays within bounds public override void update() { //zooming = FlxG.zoom; //rotating = FlxG.rotation; //follow closely or check deadzones if (target != null) { if (deadzone == null) { focusOn(target.getMidpoint()); //add getMidpoint() for FlxObjects } else { //FlxG.log("deadzone is not null"); float edge; float targetX = FlxU.ceil(target.x + ((target.x > 0) ? 0.0000001f : -0.0000001f)); float targetY = FlxU.ceil(target.y + ((target.y > 0) ? 0.0000001f : -0.0000001f)); edge = targetX - deadzone.x; if (scroll.x > edge) { scroll.x = edge; } edge = targetX + target.width - deadzone.x - deadzone.width; if (scroll.x < edge) { scroll.x = edge; } edge = targetY - deadzone.y; if (scroll.y > edge) { scroll.y = edge; } edge = targetY + target.height - deadzone.y - deadzone.height; if (scroll.y < edge) { scroll.y = edge; } //SHAKE } } //make sure we didnt go outside camera's bounds if (bounds != null) { //FlxG.log("bounds is not null"); if (scroll.x < bounds.left) { scroll.x = bounds.left; } if (scroll.x > bounds.right - width) { scroll.x = bounds.right - width; } if (scroll.y < bounds.top) { scroll.y = bounds.top; } if (scroll.y > bounds.bottom - height) { scroll.y = bounds.bottom - height; } } //update effects //shake if (_fxShakeDuration > 0) { _fxShakeDuration -= FlxG.elapsed; if (_fxShakeDuration <= 0) { _fxShakeOffset.make(); if (_fxShakeComplete != null) { _fxShakeComplete(); } } else { if ((_fxShakeDirection == SHAKE_BOTH_AXES) || (_fxShakeDirection == SHAKE_HORIZONTAL_ONLY)) { _fxShakeOffset.x = (FlxG.random() * _fxShakeIntensity * width * 2 - _fxShakeIntensity * width) * _zoom; } if ((_fxShakeDirection == SHAKE_BOTH_AXES) || (_fxShakeDirection == SHAKE_VERTICAL_ONLY)) { _fxShakeOffset.y = (FlxG.random() * _fxShakeIntensity * height * 2 - _fxShakeIntensity * height) * _zoom; } } } scroll.x -= _fxShakeOffset.x; scroll.y -= _fxShakeOffset.y; if (zooming < 1) { zooming = 1; } }
/// <summary> /// Internal random float from 0 to 1 /// </summary> /// <returns></returns> static internal float random() { return(globalSeed = FlxU.random()); }
/** * Handles fade out, fade in, panning, proximity, and amplitude operations each frame. */ override public void update() { base.update(); if (_source != null) { x = _source.getMidpoint().x; y = _source.getMidpoint().y; } float radial = 1.0f; float fade = 1.0f; //Distance-based volume control if (_target != null) { radial = (_radius - FlxU.getDistance(_target.getMidpoint(), new FlxPoint(x, y))) / _radius; if (radial < 0) { radial = 0; } if (radial > 1) { radial = 1; } } //Cross-fading volume control if (_fadeOutTimer > 0) { _fadeOutTimer -= FlxG.elapsed; if (_fadeOutTimer <= 0) { if (_pauseOnFadeOut) { pause(); } else { stop(); } } fade = _fadeOutTimer / _fadeOutTotal; if (fade < 0) { fade = 0; } } else if (_fadeInTimer > 0) { _fadeInTimer -= FlxG.elapsed; fade = _fadeInTimer / _fadeInTotal; if (fade < 0) { fade = 0; } fade = 1 - fade; } if ((autoDestroy && playCount > 0) && (_sound.State != SoundState.Paused && _sound.State != SoundState.Playing)) { _sound.Dispose(); } _volumeAdjust = radial * fade; updateTransform(); }