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

Unity C#实现蜘蛛侠风格钩索摆荡功能问题求助

蜘蛛侠钩索摆荡问题解决

问题描述

正在制作一款蜘蛛侠风格游戏,希望通过Raycast实现钩索摆荡移动,用LineRenderer显示蛛丝。目前按下按键时LineRenderer能正常显示并附着到建筑,但角色无法实现摆荡效果。

场景设置:

  • Player对象带有Rigidbody、Capsule Collider、LineRenderer组件,挂载GrapplingHook和PlayerMovement脚本
  • 建筑使用立方体预制体

现有GrapplingHook代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GrapplingHook : MonoBehaviour
{
    public Camera playerCamera;
    public LineRenderer lineRenderer;
    public float maxDistance = 100f;
    private Vector3 grapplePoint;
    private SpringJoint joint;
    private Rigidbody rb;
    
     void Start()
    {
        rb = GetComponent<Rigidbody>(); 
    }

    void Update()
    {
        if (Input.GetButtonDown("Fire1")) {
            StartGrapple();
        }
        else if (Input.GetButtonUp("Fire1")) {
            StopGrapple();
        }
    }

    void StartGrapple()
    {
        RaycastHit hit;
        if (Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, maxDistance))
        {
            grapplePoint = hit.point;
            joint = gameObject.AddComponent<SpringJoint>();
            joint.autoConfigureConnectedAnchor = false;
            joint.connectedAnchor = grapplePoint;

            float distanceFromPoint = Vector3.Distance(transform.position, grapplePoint);

            // The distance grapple will try to keep from grapple point
            joint.maxDistance = distanceFromPoint * 0.8f;
            joint.minDistance = distanceFromPoint * 0.25f;

            joint.spring = 4.5f;
            joint.damper = 7f;
            joint.massScale = 4.5f;

            lineRenderer.positionCount = 2;
        }
    }

    void StopGrapple()
    {
        lineRenderer.positionCount = 0;
        Destroy(joint);
    }

    void LateUpdate()
    {
        if (lineRenderer.positionCount > 0)
        {
            lineRenderer.SetPosition(0, transform.position);
            lineRenderer.SetPosition(1, grapplePoint);
        }
    }
    
    void FixedUpdate()
    {
        if (joint != null)
        {
            if (Input.GetKey(KeyCode.A))
            {
                rb.AddForce(-transform.right * 10f); // Add leftward force
            }
            if (Input.GetKey(KeyCode.D))
            {
                rb.AddForce(transform.right * 10f); // Add rightward force
            }
        }
    }
}

现有PlayerMovement代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
   

    // Update is called once per frame
    void Update()
    {
        if(Input.GetKey(KeyCode.W)){
            transform.position += transform.forward * Time.deltaTime*5;
        }
        
        if(Input.GetKey(KeyCode.S)){
            transform.position += -transform.forward * Time.deltaTime;
        }
    
        if(Input.GetKey(KeyCode.D)){
            transform.position += transform.right * Time.deltaTime;
        }
        
        if(Input.GetKey(KeyCode.A)){
            transform.position += -transform.right * Time.deltaTime;
        
        }
        
        if(Input.GetKey(KeyCode.Space)){
            transform.position += transform.up * 10 * Time.deltaTime;
        }
        
        if (Input.GetKey(KeyCode.RightArrow)){
            transform.Rotate(Vector3.up, 0.25f);
        }
        
        if (Input.GetKey(KeyCode.LeftArrow)){
            transform.Rotate(Vector3.down, 0.25f);
        }
        
        
    }
    
}

核心问题分析

  1. 直接修改transform.position:这种方式绕过Unity物理引擎,导致Rigidbody和SpringJoint的物理模拟失效,钩索拉力无法带动角色摆荡。
  2. 移动逻辑冲突:钩索激活时普通移动仍在运行,干扰物理摆荡效果。
  3. SpringJoint参数不合理:现有参数的拉力和弹性不足以支撑自然的摆荡体验。

修改方案

1. 重构PlayerMovement脚本

改用物理引擎驱动移动,且仅在未钩索时启用普通移动:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    private Rigidbody rb;
    public float moveSpeed = 5f;
    public float jumpForce = 8f;
    public float rotationSpeed = 100f;

    private GrapplingHook grapplingHook;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
        grapplingHook = GetComponent<GrapplingHook>();
    }

    void FixedUpdate()
    {
        // 钩索激活时禁用普通移动
        if (grapplingHook.IsGrappling())
            return;

        // 前后左右移动
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        Vector3 moveDir = transform.forward * vertical + transform.right * horizontal;
        rb.AddForce(moveDir.normalized * moveSpeed, ForceMode.Force);

        // 跳跃(简单地面检测)
        if (Input.GetKeyDown(KeyCode.Space) && Physics.Raycast(transform.position, Vector3.down, 0.6f))
        {
            rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        }
    }

    void Update()
    {
        // 旋转控制
        if (!grapplingHook.IsGrappling())
        {
            float rotateInput = Input.GetAxis("Horizontal");
            transform.Rotate(Vector3.up, rotateInput * rotationSpeed * Time.deltaTime);
        }
    }
}

2. 优化GrapplingHook脚本

添加摆荡辅助力,调整SpringJoint参数,并增加状态判断方法:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GrapplingHook : MonoBehaviour
{
    public Camera playerCamera;
    public LineRenderer lineRenderer;
    public float maxDistance = 100f;
    public float swingForce = 15f; // 摆荡辅助力
    private Vector3 grapplePoint;
    private SpringJoint joint;
    private Rigidbody rb;
    
    void Start()
    {
        rb = GetComponent<Rigidbody>(); 
        lineRenderer.positionCount = 0;
    }

    void Update()
    {
        if (Input.GetButtonDown("Fire1")) 
        {
            StartGrapple();
        }
        else if (Input.GetButtonUp("Fire1")) 
        {
            StopGrapple();
        }
    }

    void StartGrapple()
    {
        RaycastHit hit;
        if (Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, maxDistance))
        {
            grapplePoint = hit.point;
            joint = gameObject.AddComponent<SpringJoint>();
            joint.autoConfigureConnectedAnchor = false;
            joint.connectedAnchor = grapplePoint;

            float distanceFromPoint = Vector3.Distance(transform.position, grapplePoint);

            // 调整距离参数,保留蛛丝弹性
            joint.maxDistance = distanceFromPoint;
            joint.minDistance = distanceFromPoint * 0.5f;

            // 优化弹簧参数,增强拉力稳定性
            joint.spring = 12f;
            joint.damper = 4f;
            joint.massScale = 2f;

            lineRenderer.positionCount = 2;
        }
    }

    void StopGrapple()
    {
        lineRenderer.positionCount = 0;
        if(joint != null)
            Destroy(joint);
    }

    void LateUpdate()
    {
        if (lineRenderer.positionCount > 0)
        {
            lineRenderer.SetPosition(0, transform.position);
            lineRenderer.SetPosition(1, grapplePoint);
        }
    }
    
    void FixedUpdate()
    {
        if (joint != null)
        {
            // 左右摆荡控制
            if (Input.GetKey(KeyCode.A))
            {
                rb.AddForce(-transform.right * swingForce);
            }
            if (Input.GetKey(KeyCode.D))
            {
                rb.AddForce(transform.right * swingForce);
            }

            // 向前冲力,增强摆荡动量
            if (Input.GetKey(KeyCode.W))
            {
                Vector3 swingDir = (grapplePoint - transform.position).normalized;
                rb.AddForce(swingDir * swingForce * 0.7f);
            }
        }
    }

    // 供Movement脚本判断钩索状态
    public bool IsGrappling()
    {
        return joint != null;
    }
}

3. 额外设置建议

  • Player的Rigidbody设置:勾选Use Gravity,冻结旋转(避免摆荡时角色翻滚),Mass设为1-2。
  • LineRenderer设置:调整Width为0.05-0.1,添加蛛丝材质增强视觉效果。
  • 建筑Collider:确保勾选Convex(复合Collider时),保证Raycast检测正常。

内容的提问来源于stack exchange,提问作者Adele Mammadova

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 23:39:52