《拧螺丝》游戏怎么判断孔位是否可以放螺丝?

如图,请问怎么判断图中标红的孔位是否可以放螺丝?

简单,孔是圆形,铁片是多边形,判断形状是否相交就完了

2赞

太难了。。。

判断板上孔位和铁片上孔位圆心的距离

2赞

这种问题问AI就行。以下是AI回答。

基于 Cocos Creator 3.8.4 和 TypeScript,我来提供一个完整的解决方案。

  1. 首先创建一个螺丝孔的组件类:
// ScrewHole.ts
import { _decorator, Component, Node, Collider2D, Contact2DType, IPhysics2DContact } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('ScrewHole')
export class ScrewHole extends Component {
    @property(Number)
    public radius: number = 10; // 螺丝孔半径

    private _isBlocked: boolean = false;
    private _collider: Collider2D | null = null;

    start() {
        // 初始化碰撞组件
        this._collider = this.getComponent(Collider2D);
        if (this._collider) {
            this._collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
            this._collider.on(Contact2DType.END_CONTACT, this.onEndContact, this);
        }
    }

    onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
        // 检测到碰撞,标记为被遮挡
        this._isBlocked = true;
    }

    onEndContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
        // 碰撞结束,取消遮挡标记
        this._isBlocked = false;
    }

    isAvailable(): boolean {
        return !this._isBlocked;
    }
}
  1. 创建一个管理类来处理螺丝安装逻辑:
// ScrewManager.ts
import { _decorator, Component, Node, Vec2, PhysicsSystem2D, EPhysics2DDrawFlags } from 'cc';
import { ScrewHole } from './ScrewHole';
const { ccclass, property } = _decorator;

@ccclass('ScrewManager')
export class ScrewManager extends Component {
    @property([Node])
    public screwHoles: Node[] = [];

    @property(Node)
    public currentScrew: Node | null = null;

    start() {
        // 开启物理系统调试绘制(开发时可用)
        PhysicsSystem2D.instance.debugDrawFlags = 
            EPhysics2DDrawFlags.Aabb |
            EPhysics2DDrawFlags.Pair |
            EPhysics2DDrawFlags.CenterOfMass |
            EPhysics2DDrawFlags.Joint |
            EPhysics2DDrawFlags.Shape;
    }

    // 检查指定位置是否可以安装螺丝
    public canInstallScrewAt(position: Vec2): ScrewHole | null {
        for (const holeNode of this.screwHoles) {
            const hole = holeNode.getComponent(ScrewHole);
            if (!hole) continue;

            const holePos = holeNode.getWorldPosition();
            const distance = Vec2.distance(
                new Vec2(position.x, position.y),
                new Vec2(holePos.x, holePos.y)
            );

            // 检查距离和可用性
            if (distance <= hole.radius && hole.isAvailable()) {
                return hole;
            }
        }
        return null;
    }

    // 安装螺丝
    public installScrew(hole: ScrewHole) {
        if (this.currentScrew && hole.isAvailable()) {
            const screwPos = hole.node.getWorldPosition();
            this.currentScrew.setWorldPosition(screwPos);
            // 这里可以添加安装成功的动画或效果
        }
    }
}
  1. 创建遮挡物组件:
// BlockingObject.ts
import { _decorator, Component, Node, Collider2D, UITransform } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('BlockingObject')
export class BlockingObject extends Component {
    start() {
        // 确保有碰撞器组件
        const collider = this.getComponent(Collider2D);
        if (collider) {
            collider.apply();
        }
    }
}
  1. 游戏主控制脚本:
// GameController.ts
import { _decorator, Component, Node, EventTouch, Vec2, director } from 'cc';
import { ScrewManager } from './ScrewManager';
const { ccclass, property } = _decorator;

@ccclass('GameController')
export class GameController extends Component {
    @property(ScrewManager)
    public screwManager: ScrewManager | null = null;

    start() {
        // 注册触摸事件
        this.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
        this.node.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
        this.node.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
    }

    onTouchStart(event: EventTouch) {
        const touchPos = event.getLocation();
        if (this.screwManager) {
            const availableHole = this.screwManager.canInstallScrewAt(touchPos);
            if (availableHole) {
                this.screwManager.installScrew(availableHole);
            }
        }
    }

    onTouchMove(event: EventTouch) {
        // 处理拖动逻辑
    }

    onTouchEnd(event: EventTouch) {
        // 处理触摸结束逻辑
    }
}
  1. 在场景中的使用步骤:

a. 场景设置:

  1. 创建螺丝孔节点,添加 CircleCollider2D 组件和 ScrewHole 脚本
  2. 创建遮挡物节点,添加 PolygonCollider2D 组件和 BlockingObject 脚本
  3. 创建管理节点,添加 ScrewManager 和 GameController 脚本

b. 物理系统设置:

// 在项目设置中配置物理分组和掩码
// 比如在 game.ts 或初始化脚本中
PhysicsSystem2D.instance.enable = true;

c. 预制体设置:

// 螺丝预制体需要包含:
// - Sprite 组件用于显示
// - RigidBody2D 组件(可选,用于物理效果)
// - CircleCollider2D 组件用于碰撞检测

使用建议:

  1. 碰撞检测优化:
// ScrewHole.ts 中添加
private _contactCount: number = 0;

onBeginContact() {
    this._contactCount++;
    this._isBlocked = this._contactCount > 0;
}

onEndContact() {
    this._contactCount--;
    this._isBlocked = this._contactCount > 0;
}
  1. 添加视觉反馈:
// ScrewHole.ts 中添加
@property(Sprite)
private holeSprite: Sprite | null = null;

updateVisual() {
    if (this.holeSprite) {
        this.holeSprite.color = this.isAvailable() 
            ? Color.WHITE 
            : Color.GRAY;
    }
}
  1. 性能优化:
// ScrewManager.ts 中添加
private _holesMap: Map<string, ScrewHole> = new Map();

start() {
    // 预先缓存所有螺丝孔的位置信息
    this.screwHoles.forEach(hole => {
        const pos = hole.getWorldPosition();
        this._holesMap.set(`${pos.x},${pos.y}`, hole.getComponent(ScrewHole));
    });
}

这个方案使用了 Cocos Creator 的物理系统来处理碰撞检测,优点是:

  • 使用内置物理引擎,性能好
  • 可视化调试方便
  • 容易扩展和维护
  • 支持复杂的碰撞形状

注意事项:

  1. 确保正确设置物理分组和掩码
  2. 适当调整碰撞器的大小和形状
  3. 考虑添加容错范围提升游戏体验
  4. 可以添加音效和粒子效果增强反馈
  5. 建议使用对象池管理螺丝实例
  6. 考虑添加操作引导和提示
2赞

替代百分之七八十的程序员已经不远了

现在基本60%以上的代码工作都能用AI完成…用了一圈个人比较推荐Claude 3.5 Sonnet

1赞

好人当到底了,代码片段给你参考了,我是用2.4写的

1赞

// 点击圆孔时判断当前圆孔是否满足插入条件,若满足则通知螺丝
click() {
const ironArr = this.node.parent.parent.getChildByName(‘irons’).children
const overlapIronArr = []
for (let i = 0; i < ironArr.length; i++) {
const wordPos = this.node.parent.getComponent(UITransform).convertToWorldSpaceAR(this.node.getPosition())
const circleCenter = ironArr[i].getComponent(UITransform).convertToNodeSpaceAR(wordPos)

        // 多边形碰撞体的多边形点集
        const polygonPoints = ironArr[i].getComponent(PolygonCollider2D).points

        // 判断铁片是否有部分盖住孔洞(允许一定误差)
        const isContained = Intersection2D.polygonCircle(polygonPoints, v2(circleCenter.x, circleCenter.y), 30)
        if (isContained) overlapIronArr.push(ironArr[i])
    }
    if (overlapIronArr.length === 0) {
        console.log('目标孔无遮挡')
        const pos = this.node.getPosition()
        EventManager.emit('SCREWSMOVE', pos, false)
        return
    }
    // 有重叠则进一步判断是否在误差允许范围内
    let count = 0
    for (let i = 0; i < overlapIronArr.length; i++) {
        // 当前孔位于铁片的本地坐标
        const wordPos = this.node.parent.getComponent(UITransform).convertToWorldSpaceAR(this.node.getPosition())
        const circleCenter = overlapIronArr[i].convertToNodeSpaceAR(wordPos)
        // 铁片的关节信息
        const jointArr = overlapIronArr[i].getComponent('iron').haveJoint
        for (let j = 0; j < jointArr.length; j++) {
            const distance = Math.sqrt((circleCenter.x - jointArr[j].x) ** 2 + (circleCenter.y - jointArr[j].y) ** 2)
            if (distance < 5) {
                count++
                break
            }
        }
    }
    if (count === overlapIronArr.length) {
        console.log('螺丝孔与铁片孔契合')
        const pos = this.node.getPosition()
        EventManager.emit('SCREWSMOVE', pos, true, overlapIronArr)
        return
    }
    console.log('有遮挡')
}
1赞

多谢 :+1:

所有孔在一层,所有铁片在一层。所以第一步先遍历所有铁片判断孔和铁片是否相交,若无相交就是可以插入了。如果有相交:则判断铁片身上的关节(也就是铁片上的洞)孔和洞的圆心距离少于一定的误差(我设置的5)就可以认为是孔与铁片的洞重合了,都不满足,就是被挡住了。这是最简单的思路,不考虑什么性能,小游戏,无所谓

1赞

这类型的图片看着生理不适,全身发麻。有办法解决吗