You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

地图上汽车避障移动动画实现技术问询

实现带碰撞检测的汽车移动动画

现有条件

  • 一张包含若干物体的地图
  • 一张可在地图上按指定速度移动的汽车图片

需求说明

当用户点击地图时,代码需实现以下逻辑:

  • 检查点击点颜色是否在禁止颜色列表中(汽车无法穿过建筑);
  • 汽车将向点击点移动,移动过程中需持续检查前方区域是否为禁止颜色,若是则停止移动,否则继续前进。

核心实现代码

map.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            // 获取地图上的点击位置
            float clickedX = event.getX();
            float clickedY = event.getY();

            // 获取汽车当前位置
            float carX = point.getX();
            float carY = point.getY();

            // 检查点击像素是否为禁止颜色
            int pixelColor = getPixelColor((int) clickedX, (int) clickedY);
            int[] forbiddenColors = {Color.rgb(102,112,127), Color.rgb(76, 76, 76)}; // 可添加其他禁止颜色

            // 如果点击点是禁止颜色,将目标点替换为最近的可通行点
            if (isForbiddenColor(pixelColor, forbiddenColors)) {
                PointF nearestPoint = findNextNonForbiddenPoint(carX, carY, clickedX, clickedY, forbiddenColors, screenWidth, screenHeight);
                clickedX = nearestPoint.x;
                clickedY = nearestPoint.y;
            }

            // 计算汽车到目标点的距离
            float distance = calculateDistance(carX, carY, clickedX, clickedY);

            // 计算移动时长:速度为50像素/秒
            long duration = (long) (distance / 50 * 1000);

            // 创建ValueAnimator实现移动动画
            ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
            animator.setDuration(duration);

            final float startX = carX;
            final float startY = carY;
            final float endX = clickedX;
            final float endY = clickedY;

            // 设置动画更新监听器,实时更新汽车位置
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float fraction = animation.getAnimatedFraction();
                    float x = startX + fraction * (endX - startX);
                    float y = startY + fraction * (endY - startY);
                    point.setX(x);
                    point.setY(y);
                }
            });

            // 启动动画
            animator.start();
        }
        return true;
    }
});

辅助方法

// 检查颜色是否在禁止列表中
private boolean isForbiddenColor(int color, int[] forbiddenColors) {
    for (int c : forbiddenColors) {
        if (color == c) {
            return true;
        }
    }
    return false;
}

// 获取指定坐标的像素颜色
private int getPixelColor(int x, int y) {
    Bitmap bitmap = ((BitmapDrawable) map.getDrawable()).getBitmap();
    return bitmap.getPixel(x, y);
}

// 计算两点间距离(勾股定理)
private float calculateDistance(float x1, float y1, float x2, float y2) {
    float dx = x2 - x1;
    float dy = y2 - y1;
    return (float) Math.sqrt(dx * dx + dy * dy);
}

// 查找从起点到终点方向上最近的可通行点
private PointF findNextNonForbiddenPoint(float startX, float startY, float endX, float endY, int[] forbiddenColors, int screenWidth, int screenHeight) {
    // 计算方向向量
    PointF direction = new PointF(endX - startX, endY - startY);
    // 归一化方向向量
    float distance = (float) Math.sqrt(direction.x * direction.x + direction.y * direction.y);
    if (distance > 0) {
        direction.set(direction.x / distance, direction.y / distance);
    }

    // 沿方向逐像素检查,找到第一个可通行点
    PointF nextPoint = new PointF(startX, startY);
    while (isForbiddenColor(getPixelColor((int) nextPoint.x, (int) nextPoint.y), forbiddenColors)) {
        nextPoint.offset(direction.x, direction.y);
        // 超出边界则返回null
        if (nextPoint.x < 0 || nextPoint.x >= screenWidth || nextPoint.y < 0 || nextPoint.y >= screenHeight) {
            return null;
        }
    }
    return nextPoint;
}

内容的提问来源于stack exchange,提问作者Dmytro Parkhomenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 13:57:55