我想制作一个圆圈(称为核心)发光,其动画效果就像从明亮变为低光。当我使用paint.setShadow方法时,我画布上的所有东西都会发光,而不仅仅是核心圆。我怎样才能将它应用到核心?
package com.mickvdijke.circles;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.*;
import android.graphics.*;
import android.graphics.drawable.*;
import android.util.Log;
import android.content.res.*;
import android.content.*;
import android.app.*;
import com.mickvdijke.circles.framework.Game;
import com.mickvdijke.circles.framework.Screen;
public class GameScreen extends Screen {
long startTime = System.nanoTime();
World world;
Paint paint = new Paint();
RectF rect = new RectF();
GradientDrawable gradient;
Context r;
public GameScreen(Game game) {
super(game);
r = (Context) game;
world = new World(game);
world.renew();
rect.top = world.core.coords.y - world.core.shieldRadius;
rect.left = world.core.coords.x - world.core.shieldRadius;
rect.bottom = world.core.coords.y + world.core.shieldRadius;
rect.right = world.core.coords.x + world.core.shieldRadius;
paint.setAntiAlias(true);
paint.setStrokeWidth(0.0F);
// This gradient looks quite smooth, but not perfect
gradient = new GradientDrawable(GradientDrawable.Orientation.TL_BR,
new int[]{0xff001319, 0xff013e3f});
gradient.setGradientType(GradientDrawable.RADIAL_GRADIENT);
gradient.setGradientRadius((int) world.offScreenRadius);
gradient.setDither(false);
gradient.setGradientCenter(0.5F, 0.5F);
gradient.setBounds(new Rect(0, 0, game.getGraphics().getWidth(),
game.getGraphics().getHeight()));
paint.setTextSize(((float)game.getGraphics().getHeight()) / 16F);
paint.setTextAlign(Paint.Align.CENTER);
}
@Override
public void update(float deltaTime) {
world.update(deltaTime);
}
@Override
public void present(float deltaTime) {
Canvas c = game.getGraphics().getCanvas();
gradient.draw(c);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
if(world.core.shieldEnergy > 0.0F)
{
paint.setColor(0xff003cca);
paint.setAlpha((int) (80.0F +
(255.0F - 80.0F) * world.core.shieldEnergy));
c.drawCircle(world.core.coords.x, world.core.coords.y,
world.core.shieldRadius, paint);
paint.setAlpha(255);
}
paint.setColor(0xffea2533);
paint.setShadowLayer(30, 0, 0, Color.RED);
c.drawCircle(world.core.coords.x, world.core.coords.y,
world.core.maxRadius * world.core.health,
paint);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(0xffffffff);
paint.setStrokeWidth(Core.SHIELD_WIDTH);
c.drawArc(rect, (360.0F - world.core.angle),
(360.0F - world.core.GAP_ANGLE), false, paint);
paint.setStrokeWidth(0.0F);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
Iterator<Dot> iterator = world.dots.iterator();
while(iterator.hasNext())
{
int color = 0;
Dot dot = iterator.next();
if(dot.type == Dot.Type.Enemy)
color = 0xffe2192e;
else if(dot.type == Dot.Type.Health)
color = 0xff19dbe2;
else if(dot.type == Dot.Type.Shield)
color = 0xff003cca;
paint.setColor(color);
c.drawCircle(dot.coords.x, dot.coords.y,
dot.maxRadius * dot.energy, paint);
}
if(world.state == World.GameState.Running)
drawMessage(world.getTime(), c);
else if(world.state == World.GameState.Ready)
drawMessage(r.getString(R.string.ready), c);
else if(world.state == World.GameState.Paused)
drawMessage(r.getString(R.string.paused), c);
else if(world.state == World.GameState.GameOver)
drawMessage(r.getString(R.string.game_over)+
"\n"+
r.getString(R.string.your_time) + " " + world.getTime() +
"\n\n" + r.getString(R.string.game_url), c);
}
private void drawMessage(String message, Canvas c)
{
float y = paint.getTextSize();
for(String line: message.split("\n"))
{
// Draw black stroke
paint.setStrokeWidth(2F);
paint.setColor(0xff000000);
paint.setStyle(Paint.Style.STROKE);
c.drawText(line, c.getWidth()/2F, y, paint);
// Draw white text
paint.setStrokeWidth(0.0F);
paint.setColor(0xffffffff);
paint.setStyle(Paint.Style.FILL);
c.drawText(line, c.getWidth()/2F, y, paint);
y += paint.getTextSize();
}
}
@Override
public void pause() {
world.state = World.GameState.Paused;
}
@Override
public void resume() {
}
@Override
public void dispose() {
}
}
&#13;
package com.mickvdijke.circles;
import java.util.*;
import android.util.*;
import com.mickvdijke.circles.framework.Audio;
import com.mickvdijke.circles.framework.Game;
import com.mickvdijke.circles.framework.Graphics;
import com.mickvdijke.circles.framework.Input;
import com.mickvdijke.circles.framework.Sound;
/* I should have used pools for my objects not to make garbage
* collector angry. As it freezes the game sometimes,
* I avoided some object creations. However, it doesn't help.
*/
public class World
{
Random random = new Random();
Game game;
private final int DOTS_COUNT = 10;
// In this case ArrayList is better than LinkedList:
// list will never be resized.
public List<Dot> dots = new ArrayList<Dot>(DOTS_COUNT);
public Core core = new Core();
public float offScreenRadius;
private final float SHIELD_FACTOR = 20.0F;
private final float ENERGY_FACTOR = 6.0F;
private float time = 0.0F; // in seconds
public enum GameState {Ready, Running, Paused, GameOver}
public GameState state = GameState.Ready;
private float difficulty = 0.04F; // Max 0.1F
// Sounds
// Dot collides with core
Sound coreHurt;
Sound coreHealth;
Sound coreShield;
// Dot collides with shield
Sound shieldCollision;
Sound gameOver;
public World(Game game)
{
this.game = game;
Graphics g = game.getGraphics();
// Construct core
core.coords = new VectorF((float) g.getWidth() / 2,
(float) g.getHeight() / 2);
core.shieldRadius = (float) g.getWidth() / 4;
core.maxRadius = core.shieldRadius * 0.7F;
core.angle = 0.0F;
core.health = 1.0F;
core.shieldEnergy = 0.0F;
// Set offScreenRadius
offScreenRadius = (float) Math.hypot((double) g.getWidth() / 2,
(double) g.getHeight() / 2);
// Max dot radius (when it's energy is 1.0F)
Dot.maxRadius = core.maxRadius / 8.0F;
loadSounds();
}
private void loadSounds()
{
Audio a = game.getAudio();
coreHurt = a.newSound("core_hurt.wav");
coreHealth = a.newSound("core_health.wav");
coreShield = a.newSound("core_shield.wav");
shieldCollision = a.newSound("shield_collision.wav");
gameOver = a.newSound("game_over.wav");
}
// Restart the game
public void renew()
{
dots.clear();
core.health = 1.0F;
core.shieldEnergy = 0.0F;
time = 0.0F;
state = GameState.Ready;
difficulty = 0.04F;
generateStartDots(DOTS_COUNT);
}
// Add randomness
private void generateStartDots(int count)
{
for(int i = 0; i < count; i++)
{
generateNewDot(true);
}
}
private VectorF generateNewDotCoordsInRandomPlace()
{
double angle = random.nextDouble() * 2 * Math.PI;
VectorF coords = new VectorF((float) Math.cos(angle),
(float) Math.sin(angle));
coords = coords.multiply(core.shieldRadius + (offScreenRadius -
core.shieldRadius) * random.nextFloat());
return coords;
}
public void update(float deltaTime)
{
if(state == GameState.Ready)
updateReady(deltaTime);
if(state == GameState.Running)
updateRunning(deltaTime);
if(state == GameState.Paused)
updatePaused(deltaTime);
if(state == GameState.GameOver)
updateGameOver(deltaTime);
}
private void doInput()
{
float orientAngle = game.getInput().getAzimuth();
if(game.getInput().isTouchDown())
{
double touchX = (double) game.getInput().getTouchX();
double touchY = (double) game.getInput().getTouchY();
core.angle =
// Y-axis is inverted. See checkCollisionWithShield(...)
// method
normAngle(((float) (Math.atan2(-(touchY - core.coords.y),
touchX - core.coords.x) / (Math.PI * 2) *
360.0)) - Core.GAP_ANGLE/2F);
}
else
{
core.angle = stabilizeAngle(orientAngle - Core.GAP_ANGLE/2F,
core.angle, 8F);
}
}
// Removes accelerometer noise and makes
// core shield rotate smooth when user touches / untouches
// the screen (and game switch accelerometer / touchscreen control).
// Stabilisation increases, as factor value becomes larger.
private float stabilizeAngle(float real, float current, float factor)
{
real = normAngle(real);
current = normAngle(current);
// Stabilisation should choose shortest way
// (is it better to rotate clockwise or counterclockwise?)
if(current - real > 180F)
real += 360;
if(real - current > 180F)
real -= 360;
// (current + current + current ... + real) / numberOfElements
return normAngle((current * factor + real) / (factor + 1F));
}
private void updateReady(float deltaTime)
{
if(checkTouchUp() || checkMenuUp())
state = GameState.Running;
}
private boolean checkTouchUp()
{
for(Input.TouchEvent event : game.getInput().getTouchEvents())
{
if(event.type == Input.TouchEvent.TOUCH_UP)
return true;
}
return false;
}
private boolean checkMenuUp()
{
for(Input.KeyEvent event : game.getInput().getKeyEvents())
{
if(event.keyCode == android.view.KeyEvent.KEYCODE_MENU)
{
if(event.type == Input.KeyEvent.KEY_UP)
return true;
}
}
return false;
}
private void updatePaused(float deltaTime)
{
if(checkTouchUp() || checkMenuUp())
state = GameState.Running;
}
private void updateGameOver(float deltaTime)
{
if(checkTouchUp() || checkMenuUp())
renew();
}
private void updateRunning(float deltaTime)
{
checkTouchUp(); // Just to clear touch event buffer
if(checkMenuUp())
state = GameState.Paused;
countTime(deltaTime);
doInput();
generateNewDots(DOTS_COUNT);
handleCollisions();
moveDots(deltaTime);
decreaseShieldEnergy(deltaTime);
}
private void decreaseShieldEnergy(float deltaTime)
{
if(core.shieldEnergy > 0.0F)
{
core.shieldEnergy -= deltaTime / SHIELD_FACTOR;
if(core.shieldEnergy < 0.0F)
core.shieldEnergy = 0.0F;
}
}
private void generateNewDots(int neededCount)
{
float rand = random.nextFloat();
if(neededCount > dots.size())
generateNewDot(false);
}
private void increaseDifficulty()
{
difficulty += 0.00005F;
}
private void generateNewDot(boolean atStart)
{
float linearSpeed = 10.0F * difficulty;
Dot dot = new Dot();
if(atStart)
{
dot.coords = generateNewDotCoordsInRandomPlace();
}
else
{
dot.coords = generateNewDotAtOffScreenRadius();
increaseDifficulty();
}
VectorF speed = new VectorF(
linearSpeed * (-dot.coords.x / dot.coords.length()),
linearSpeed * (-dot.coords.y / dot.coords.length()));
dot.speed = speed;
dot.coords.addToThis(core.coords);
dot.energy = random.nextFloat();
if(dot.energy <= 0.3F)
dot.energy = 0.3F;
float typeRand = random.nextFloat();
Dot.Type type;
if (typeRand >= 0.9)
type = Dot.Type.Shield;
else if (typeRand >= 0.8)
type = Dot.Type.Health;
else
type = Dot.Type.Enemy;
dot.type = type;
dots.add(dot);
}
private void countTime(float deltaTime)
{
time += deltaTime;
}
public String getTime()
{
int seconds = (int) time;
int minutes = seconds / 60;
seconds %= 60;
String result = "";
if(minutes > 0)
result += minutes + ":";
result += String.format("%02d", seconds);
return result;
}
private VectorF generateNewDotAtOffScreenRadius()
{
float angle = random.nextFloat() * ((float)(2 * Math.PI));
VectorF coords =
new VectorF(offScreenRadius * ((float) Math.cos(angle)),
offScreenRadius * ((float) Math.sin(angle)));
return coords;
}
private void moveDots(float deltaTime)
{
for(Dot dot : dots)
{
dot.coords.addToThis(dot.speed.x * deltaTime * 100.0F,
dot.speed.y * deltaTime * 100.0F);
}
}
private void handleCollisions()
{
Iterator<Dot> iterator = dots.iterator();
while(iterator.hasNext())
{
handleCollision(iterator.next(), iterator);
}
}
private void handleCollision(Dot dot, Iterator<Dot> iterator)
{
float lengthToCoreCenter = (float)
Math.hypot((double)(dot.coords.x - core.coords.x),
(double)(dot.coords.y - core.coords.y));
if(Math.abs(lengthToCoreCenter -
core.shieldRadius) <= dot.maxRadius * dot.energy +
Core.SHIELD_WIDTH)
checkCollisionWithShield(dot, iterator);
else if (lengthToCoreCenter - core.maxRadius * core.health <=
dot.maxRadius * dot.energy)
handleCollisionWithCore(dot, iterator);
}
private void checkCollisionWithShield(Dot dot, Iterator<Dot> iterator)
{
// I normalize (move into (0; 360) interval) angles
// in some places. Don't know if it's needed.
if(core.shieldEnergy > 0.0F)
{
iterator.remove();
shieldCollision.play(dot.energy);
game.getVibration().vibrate(30);
}
else
{
// Pay attention at -v.y! Y-axis is inverted,
// because it points downwards.
float dotAngle = (float) Math.atan2((double) -
(dot.coords.y - core.coords.y),
(double) (dot.coords.x - core.coords.x));
dotAngle = dotAngle / (((float) Math.PI) * 2.0F) * 360.0F;
dotAngle = normAngle(dotAngle);
// For example, dotAngle = 3, and core.angle = 365
// We need to solve this somehow:
core.angle = normAngle(core.angle);
while(dotAngle < core.angle)
{
dotAngle += 360.0F;
}
// OK, and check if dotAngle is within the gap
if(!((dotAngle > core.angle) &&
(dotAngle < core.angle + core.GAP_ANGLE)))
{
iterator.remove();
shieldCollision.play(dot.energy);
game.getVibration().vibrate(30);
}
}
}
private float normAngle(float angle)
{
float angle2 = angle;
while(angle2 < 0.0F)
angle2 += 360.0F;
while(angle2 > 360.0F)
angle2 -= 360.0F;
return angle2;
}
private void handleCollisionWithCore(Dot dot, Iterator<Dot> iterator)
{
if(dot.type == Dot.Type.Enemy)
{
core.health -= dot.energy / ENERGY_FACTOR;
if(core.health < 0.0F)
{
state = GameState.GameOver;
gameOver.play(1F);
game.getVibration().vibrate(10);
game.getVibration().vibrate(40);
game.getVibration().vibrate(100);
core.health = 0.0F;
}
coreHurt.play(dot.energy);
}
else if (dot.type == Dot.Type.Health)
{
core.health += dot.energy / ENERGY_FACTOR;
if(core.health > 1.0F)
{
core.health = 1.0F;
}
coreHealth.play(dot.energy);
}
else if(dot.type == Dot.Type.Shield)
{
core.shieldEnergy += dot.energy;
if(core.shieldEnergy > 1.0F)
core.shieldEnergy = 1.0F;
coreShield.play(dot.energy);
}
iterator.remove();
game.getVibration().vibrate(30);
}
}
&#13;