Unity RPG 黑暗之光 问题记录 上 (1-63 地形场景 角色选择 行走 相机跟随、旋转、缩放 任务系统 面板栏 背包系统 状态系统)

001 游戏预览和介绍

职业选择
鼠标点击移动 旋转 缩放

药品 装备 任务NPC
状态 装备 技能 存档

代码跟原视频的有所改动,主要是主角行为逻辑

002 导入场景资源,搭建场景

3方资源:RPG NGUI StandarAssets
进去关闭自动生成

(问题)模型贴图丢失

在这里插入图片描述
改变Shader类型(这么多个,一个个改),然后重新拖地图。原类型Toon/Basic
在这里插入图片描述

(问题)地形贴图资源丢失

paint texture移除重新画
重新画的贴图很模糊,法线也不对

(问题)地图是灰色的

Terrain数据文件丢失,重新做吧,已经是官方的初始文档了
第二种可能,照明设置中。开启了雾气
后面发现第3种可能,Unity的优化,让场景在远的变这样,拉近点又恢复好了
在这里插入图片描述

(问题)Texture Paint不上去

地表的Texture个还是刷不上去
Unity 3d ,地形上刷不了纹理的原因

(了解)Terrain的Tree选项卡

Tree选项卡就是写入Terrain中,不像直接拖入场景中可以移动
画笔大小要大于树密度,可以体现出随机疏朗,不然后太整齐
在这里插入图片描述

003 给场景添加光,设置鼠标指针图片

(了解)默认鼠标

在这里插入图片描述

004 给场景添加水面和天空盒子

(了解)天空盒子 skyBox

在这里插入图片描述

005 实现镜头缓慢拉近的效果

找不到编辑/照明设置

移到window/渲染/渲染设置(渲染或照明)中‘
在这里插入图片描述

006 使用NGUI和白色图片给场景添加渐显效

NGUI toolbar只有文字

unity 2018.4.32f1对应的NGUI版本
NGUI Next-Gen UI v3.12.1 (Jun 29, 2018)
在这里插入图片描述
有个情况是我把所有的Assets都新建了个文件夹来保存,之后重新导到根目录,可以了。
在这里插入图片描述

Atlas图集

这里是NGUI的Button的图片的添加,图片要加入图集内,需要图集。
打开NGUI/打开/图集编辑器

(问题)新建失败

unity nGUI atlas 将文件保存在项目资源文件夹
在这里插入图片描述

(了解)

对比可用的图集,有多了材质,texture两个属性
在这里插入图片描述

(可行)新建 == 先复制再修改

对比可用的图集,有生成.mat .png .prefab文件。
所以尝试着复制这三个,再修改。(可行)
保存的位置一般是资源同文件夹
在这里插入图片描述

点x,再点Delete。删多个点多次x,Delete只需点一次
在这里插入图片描述

007 设计开始的界面,开始加载按钮和logo

(按钮)NRPG按钮 == Sprite + 触发器 + UIButton

触发器大小默认为0,要撑起来,跟按钮一样大

(按钮显示隐藏)

对象的添加,有说Tag的是,但觉得浪费。直接public
.SetActive大写

一开始按就显示加载游戏等的按钮,但想动画结束后再显示,开始时加计时器

    {
        time -= Time.deltaTime;

        if (Input.anyKeyDown && time<=0)//因为开头有个动画,time秒后
        {
            newGameButton.SetActive(true);
            loadGameButton.SetActive(true);
            pressAnyKey.SetActive(false);
        }
    }

PlayerPrefs

PlayerPrefs是系统自带的,往里面存键值对

    void OnNewGame()//开始游戏
    {
        PlayerPrefs.SetInt("LoadFrom", 0);
    }
    void OnLoadGame()//加载游戏
    {
        PlayerPrefs.SetInt("LoadFrom", 1);
    }

(按钮声音)AudioSource没声音

检查
音频文件(文件夹中播放看看)
重启软件
AudioSource叉出去,重新加
还有以下:

ProjectSetting(忘了要看什么,音量不为0?)

在这里插入图片描述

游戏Mute(关闭静音)

在这里插入图片描述

组件Mute(关闭静音)

在这里插入图片描述

组件音量(最大试试)

在这里插入图片描述

011 开始角色创建场景,导入模型和UI资源

导入Moedl
导入,加入角色选择的UI到RPG的图集里面(打开图集,选择UI后点击Add/Update)

NGUI按钮点击无效

将UIRoot的相机类型设为UI
在这里插入图片描述

NGUI 工具栏拖不出东西,示例场景很多损坏

看是实例场景1,2,7,9,10,12,13,14,15和tutorial都是坏的
在这里插入图片描述

(失败)重新导

场景恢复了,但是toolbar还是拖不出东西

(失败)更换NGUI版本

我的unity是2018.4.32f1
不能用的NGUI是NGUI Next-Gen UI v3.12.1
可以用的NGUI是NGUI Next-Gen UI v3.11.2

可以用的NGUI也有点不正常,样式不正常
在这里插入图片描述

(解决)过了一会又没了

发现只要我更新了自己生成的RPG资源,就一定出现NGUI的错,也就是两者起冲突了。
然后看着下图,有又是trim,又是replace,就全取消试试,暂时解决问题

一个文件夹一个图集,不要跨文件夹吧、(也许有这个原因)
NGUI放根目录Assets吧(很想跟其他导入的资源放一块)
在这里插入图片描述

NGUI的输入框和其他的并存了,PrefabTool也能拖拽

在这里插入图片描述

图集必备3文件,只有2个

Prefab mat 缺少png

应该从资料中找一下.png那张图拖进去。或者重新导
在这里插入图片描述

删除NGUI后,之前通过NGUI的图集编辑器制作的图集损坏

重新add进texture

(导入出现报错)

Shader error in ‘Transparent/Refractive’: Too many texture interpolators would be used for ForwardBase pass (9 out of max 8), try adding #pragma target 3.0 at line 41
在这里插入图片描述
加了确实没报错,不过没用到
Unity shader error: “Too many texture interpolators would be used for ForwardBase pass”

UI和场景的相机

分开的照,合并显示在屏幕上
在这里插入图片描述

3点打光

材质 standard
在这里插入图片描述

015 控制角色的上一个和下一个的选择

切换角色,要挂在按钮上
我想读文件的,即读取上把文件夹当作组件(但没学过,电脑里的游戏,直接改文件夹中的数据的日子啊 )
在这里插入图片描述


    public GameObject[] characterPrefabArray;
    public int index=0;//看的
    private GameObject[] goArray;
    // Start is called before the first frame update
    void Start()
    {

        if (characterPrefabArray.Length != 0)
        {
            //实例 隐藏
            goArray = new GameObject[characterPrefabArray.Length];
            for (int i = 0; i < characterPrefabArray.Length; i++)
            {
                goArray[i] = Instantiate(characterPrefabArray[i], transform.position, transform.rotation);
                goArray[i].SetActive(false);
                goArray[i].transform.parent = transform;
            }
            goArray[0].SetActive(true);
        }


    }

    public void PreCharacter()
    {
        goArray[index].SetActive(false);

        index = index == 0 ? (characterPrefabArray.Length - 1) : --index;
        goArray[index].SetActive(true);
    }
    public void NextCharacter()
    {
        goArray[index].SetActive(false);

        index = index == characterPrefabArray.Length - 1 ? 0 : ++index;
        goArray[index].SetActive(true);
    }

016 处理名称的输入和场景的切换

(问题)2D盒状触发器导致的NGUI的输入框(UIInput)不能点击输入

问题描述
上下,选择按钮都可以动,只有输入框不能点击

流程介绍
节A挂一个UIInput的节点,来接收输入框Label;
节点A被脚本引用,用value来取里面的字符串存在PlayerPrefs里面

错误排查
节点A是UIRoot没问题,是输入框根节点有问题(其他场景不会)。
因素:UIRoot、输入框根节点的Depth是0,Label是1,都一样
因素:碰撞器,结果是盒状触发器的问题,要选2D盒状触发器
因素:该案例中submit可以不用写
因素:挂UIInput的节点在运行后,会自动加上UI Input On GUI的脚本

    public void LoadScene()
    {
        PlayerPrefs.SetString("SelectedPlayerName", characterNameInput.value);
        PlayerPrefs.SetInt("SelectedPlayerIndex", index);

        print(PlayerPrefs.GetInt("SelectedPlayerIndex"));
        print(PlayerPrefs.GetString("SelectedPlayerName"));
        SceneManager.LoadScene(2);//2是数据,要分离开,但我还没学过
    }

(了解) Invalid texture used for cursor - check importer settings or texture creation. Texture must be RGBA32, readable, have alphaIsTransparency enabled and have no mip chain.

我改这就消失了
Invalid texture used for cursor - check importer settings or texture creation. Texture must be RGBA3
在这里插入图片描述

(情况)unity保存,还有星号,在没有报错,警告的情况下

在这里插入图片描述

(问题)NGUI层黑色,运行后什么都没有

修改NGUI的相机剔除遮罩,选择default 或者 Everything(也是default起作用)
在这里插入图片描述

(了解)深度,深度里的会被照进来

在这里插入图片描述

018 标签管理

有了下面的文件,Tags.group “Group”,并且敲出Tags后是有提示的(Tags.p 可能就有 Tags.player 的提示)
好处是:写得快,不写错

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

public class Tags : MonoBehaviour
{
    public const string group = "Group";
    public const string player = "Player";


}

019 实现角色行走的点击效果

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            print("点击");
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//点生成射线
            RaycastHit hit;
            bool isCollided = Physics.Raycast(ray, out hit);//是否碰到
            if (isCollided && hit.collider.tag==Tags.ground)
            {
                OnClickEffect(effectPrefab, hit.point);
            }
        }
    }

    void OnClickEffect(GameObject effectPrefab, Vector3 position)
    {
        print("Move");
        Instantiate(effectPrefab, position, Quaternion.identity);
    }

(问题)空引用

MainCamera的标签
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition)会报错 空引用

(问题)位置

不是hit.collider.transfrom.position(会跑到世界坐标原点),而是 hit.point

020 控制主角移动的朝向

(问题)鼠标下点,达不到鼠标按着滑动时,玩家一直朝向位置的目的

视频时
鼠标下点,isMoving=true
鼠标上抬,isMoving=false
而鼠标下点到上抬的过程是,鼠标一直按着的时候,这过程isMoving=true
这样做到了下点按着,更新朝向,抬起不更新

上面做的后果是Update实例太多次,按着鼠标时计时

    void Update()
    {
        if (Input.GetMouseButtonDown(0))//鼠标下点
        {
            notMouseButtonUp = true;
            targetPosition = positionByRay2Tag(Input.mousePosition, Tags.ground);
            prefabByPosition(effectPrefab, targetPosition);

        }
        else if (Input.GetMouseButtonUp(0))//鼠标上抬
        {
            notMouseButtonUp = false;
        }

        
        if (notMouseButtonUp)//鼠标按着
        {
            targetPosition = targetPosition = positionByRay2Tag(Input.mousePosition, Tags.ground);          
            gameObject.transform.LookAt(targetPosition);
            //计时1秒实例 
            mouseButtonEffectTimer += Time.deltaTime;
            if (mouseButtonEffectTimer > mouseButtonEffectTime)
            {
                mouseButtonEffectTimer = 0f;
                prefabByPosition(effectPrefab, targetPosition);
            }
        }

    }
    //判断 mouseDownPosition发出的射线是否撞到标签为tag的物体,是的返回hit的位置
    Vector3 positionByRay2Tag(Vector3 mouseDownPosition, string tag)
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//点生成射线
        RaycastHit hit;
        bool isCollided = Physics.Raycast(ray, out hit);//是否碰到
        if (isCollided && hit.collider.tag == Tags.ground)
        {
            targetPosition = hit.point;
            return hit.point;
        }

        return Vector3.zero;//默认值
    }
    void prefabByPosition(GameObject prefab, Vector3 position)//在该位置实例预制体
    {
        Instantiate(prefab, position, Quaternion.identity);
    }

021 控制主角的移动

transform.Translate(Time.deltaTime * speed)

    void Move()
    {
        float stopDistanceNearTargetPosition = 0.2f;//可以停下的距离
        float distance = Vector3.Distance(transform.position, targetPosition);
        if (targetPosition != Vector3.zero && distance > stopDistanceNearTargetPosition)
        {
            transform.Translate(Vector3.forward * Time.deltaTime * speed);
        }
    }

角色控制器SimpleMove(speed)

    void MoveByCharacterController()
    {
        float stopDistanceNearTargetPosition = 0.2f;//可以停下的距离
        float distance = Vector3.Distance(transform.position, targetPosition);
        if (targetPosition != Vector3.zero && distance > stopDistanceNearTargetPosition)
        {
            characterController.SimpleMove(transform.forward  * speed);
        }
    }

022 控制主角的移动动画的播放

    void Update()
    {
        if (!MoveByMouse._instance.notMouseButtonUp)
        {
            animation.CrossFade("Sword-Idle");
        }
        else if (MoveByMouse._instance.notMouseButtonUp)
        {
            animation.CrossFade("Sword-Run");
        }
    }

(问题)玩家会出现头朝地跑

y是玩家的y

 gameObject.transform.LookAt(new Vector3(targetPosition.x, transform.position.y, targetPosition.z) );

在这里插入图片描述

(问题)按着鼠标才出现run

因为是根据鼠标状态,而不是Vetor3.Distance

(了解)LateUpdate

Update设置状态
LateUpdate设置动画

(代码,了解)定义

public enum State
{
    Idle,
    Walk,
    Run
}

枚举变量的public会有下拉项的样式
在这里插入图片描述

(代码)设置状态

    void MoveByCharacterController()
    {
        float stopDistanceNearTargetPosition = 0.2f;//可以停下的距离
        float distance = Vector3.Distance(transform.position, targetPosition);
        if (targetPosition != Vector3.zero && distance > stopDistanceNearTargetPosition)
        {
            state = State.Run;
            characterController.SimpleMove(transform.forward * speed);
        }
        else
        {
            state = State.Idle;
        }
    }

(代码)根据状态设置动画

   private new Animation animation;
    private State state = State.Idle;
    private MoveByMouse player;
    
    // Start is called before the first frame update
    void Start()
    {
        animation = GetComponent<Animation>();
        player = GetComponent<MoveByMouse>();
    }

    // Update is called once per frame
    void LateUpdate()
    {
        if (player.state==State.Idle)
        {
            animation.CrossFade("Sword-Idle");
        }
        else if (player.state == State.Run)
        {
            animation.CrossFade("Sword-Run");
        }
    }

023 修复bug完善主角的移动控制

(问题)点击后一直向前走,主要在崎岖地形

尤其是y值的过高,导致距离一直处于不到达的状态,该情况下玩家一直forward
在这里插入图片描述

(问题) 脚丫悬空

025 使用鼠标滑动控制相机视野的拉近和拉远

代码

	public GameObject player;
	private Vector3 offset;

	//拉近
	public float scrollSpeed = 10f;//拉伸速度
	public float distance;//实际拉伸
	public float minDistance=3.2f;//最大拉伸
	public float maxDistance=30f;//最小拉伸

	// Use this for initialization
	void Start () {
		offset = transform.position - player.transform.position;
		//offset = new Vector3(0, offset.y, offset.z);//x=0,左右不偏移
	}
	
	// Update is called once per frame
	void Update () {

		transform.position =player.transform.position+ offset;
		transform.LookAt(player.transform.position);

		offset = ScrollView();
	}

	Vector3 ScrollView()//相机拉伸
	{
		distance = offset.magnitude;
		distance += scrollSpeed * Input.GetAxis("Mouse ScrollWheel");

		if (distance < minDistance)
		{
			distance = minDistance;
		}
		else if (distance > maxDistance)
		{
			distance = maxDistance;
		}
		offset = offset.normalized * distance;

		return offset;
	}

(了解)Clamp夹紧

		distance = Mathf.Clamp(distance, minDistance, maxDistance);
		//代替下面的

		if (distance < minDistance)
		{
			distance = minDistance;
		}
		else if (distance > maxDistance)
		{
			distance = maxDistance;
		}

027 控制视野的左右旋转

(了解)旋转影响相机的位置,offset发生变化

		void RotateView()
		{			
			if (Input.GetMouseButtonDown(1))
			{
				isRotate = true;
			}
			if (Input.GetMouseButtonUp(1))
			{
				isRotate = false;
			}

			if (isRotate)
			{
				transform.RotateAround(player.transform.position, Vector3.up, rotateSpeed*Input.GetAxis("Mouse X"));
			}
			offset = transform.position - player.transform.position;//旋转影响相机的位置,offset发生变化
		}

028 控制视野的上下旋转和范围限制

代码

按住鼠标右键旋转,上下角度钳制(10,80)
值的记录要在RotateAround之前

	void RotateView()//相机旋转
		{			
			if (Input.GetMouseButtonDown(1))
			{
				isRotate = true;
			}
			if (Input.GetMouseButtonUp(1))
			{
				isRotate = false;
			}
			if (isRotate)
			{
				//记录
				Vector3 originalPosition = transform.position;
				Quaternion originalRotation = transform.rotation;
				//赋值
				transform.RotateAround(player.transform.position, transform.up, rotateSpeed * Input.GetAxis("Mouse X")); transform.RotateAround(player.transform.position, transform.right, rotateSpeed * Input.GetAxis("Mouse Y"));
				transform.RotateAround(player.transform.position, transform.right, rotateSpeed * Input.GetAxis("Mouse Y"));

				//限制范围
				if (transform.eulerAngles.x < 10 || transform.eulerAngles.x > 80)
				{
					print ("超范围了");
					transform.position = originalPosition;
					transform.rotation = originalRotation;
				}			                                           
			}

			offset = transform.position - player.transform.position;//旋转影响相机的位置,offset发生变化
		}

030 在场景中添加老爷爷这个NPC

(问题)模型的材质丢失,呈粉红色

该材质的类型standard
在这里插入图片描述

031 设计任务对话框的背景

(问题,严重)图集信息丢失,造成要重新加上去

丢失一次,尤其有按钮的,加图片很麻烦
发现是复制的场景,原来的没坏,但不清楚现在的为什么坏了
在这里插入图片描述

(问题)重影

有关透视
在这里插入图片描述

在这里插入图片描述

(问题)位置不对

场景中看transfrom看是680到150,就可以达到效果
Tween看是700到-1410才能达到想要的位置
在这里插入图片描述

032 任务系统-设计任务的内容

(问题)Label在unity字体下不显示

在这里插入图片描述
在这里插入图片描述
Material材质那里加一次字体,显示黑色字体,又None掉,白色,正常显示
感觉像给NGUI提示,快用字体啊
在这里插入图片描述

(问题)多个面板

正常只有1个(我也只有一个),现在是运行后左边有一个不动的,右边那个是Tween Position过来的加粗样式

(问题)NGUI层出现Terrain的图像

在这里插入图片描述
太长照到后面的
调相机的远近
在这里插入图片描述
近最小0远最小0.01
在这里插入图片描述

(了解)场景中图标太大

Unity3D之NGUI基础2:使用NGUI

(了解)按钮所需的组件

NGUI的按钮 等于 Sprite + UIButton + Play Sound + 2D触发器(看相机是2D还是3D,系统自动切)

(解决)按钮点击不了

触发器大小默认为0,没调
用attackh自动铺张

(了解)GUI图层组件弃用

unity3d 2018.3.10f1不能使用GUI图层
在这里插入图片描述

(问题)NGUI的坐标不一致

场景中人物板从x610到x250
NGUI只显示了1/3,只有x610到x-770效果才一样

看别的视频,unity单位是米,但这里单位是像素
上面的距离是360,1380,也就是1米 == 2像素 ?
在这里插入图片描述

(解决)NGUI的按钮不能点击

对比了很多下,删掉新建最方便

(解决)UICamera.hoveredObject==null后点击地面,人物不移动

在unity中使用UICamera.hoveredObject==null后游戏点击地面无反应和鼠标点击穿透UI

    private void MouseDownEvent()
    {
       // if (UICamera.hoveredObject != null) return;
        if (UICamera.isOverUI) return;

        print("鼠标下按:" + UICamera.hoveredObject);

        isMouseButtonPress= true;
        targetPosition = GetTargetPosition(Input.mousePosition);
        Instantiate(effectPrefab, targetPosition, Quaternion.identity);
    }

33 点击NPC弹出任务板

脚本挂NPC,我原想挂任务板,后面也该挂任务板


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

public class Quest : MonoBehaviour
{

    public GameObject quest;

    private void OnMouseOver()
    {
        if (Input.GetMouseButtonDown(0))
        {
            print("点击");  
            ShowQuest();
        }
    }

    void ShowQuest()
    {

        quest.GetComponent<TweenPosition>().PlayForward(); 
    }
    public void DisableQuest()
    {
        quest.GetComponent<TweenPosition>().PlayReverse();
    }
}

36 鼠标指针管理系统

图片,焦点,软硬件
方法要被调用,不然…

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

public class GameSettings : MonoBehaviour
{
    public Texture2D attackCursor;
    public Texture2D lockTargetCursor;
    public Texture2D normalCursor;
    public Texture2D npcTalkCursor;
    public Texture2D pickCursor;
    public static GameSettings _instance;

    private void Awake()
    {
        _instance = this;
    }

    public void SetNormalCursor()
    {
        Cursor.SetCursor(normalCursor, Vector2.zero, CursorMode.Auto );//图形,焦点
    }
    public void SetLockTargetCursor()
    {
        Cursor.SetCursor(lockTargetCursor, Vector2.zero, CursorMode.Auto);//图形,焦点
    }
    public void SetAttackCursor()
    {
        Cursor.SetCursor(attackCursor, Vector2.zero, CursorMode.Auto);//图形,焦点
    }
    public void SetNpcTalkCursor()
    {
        Cursor.SetCursor(npcTalkCursor, Vector2.zero, CursorMode.Auto);//图形,焦点
    }
    public void SetPickCursorr()
    {
        Cursor.SetCursor(pickCursor, Vector2.zero, CursorMode.Auto);//图形,焦点
    }

}

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

public class Npc : MonoBehaviour
{
    private void OnMouseEnter()
    {
        GameSettings._instance.SetLockTargetCursor();
    }
    private void OnMouseExit()
    {
        GameSettings._instance.SetNormalCursor();
    }
}

(了解)脚本有默认设置

在这里插入图片描述

037 开发功能面板

01 玩家点击 Bar上的按钮

在这里插入图片描述

02 触发了总节点上的函数去打开面板

    public void OnStatusButtonClick()
    {
        StatusPannel._instance.ShowWindow();
    }
    public void OnBagButtonClick()
    {
        BagPannel._instance.ShowWindow();
    }
    public void OnEquipmentButtonClick()
    {
        EquipPannel._instance.ShowWindow();
    }
    public void OnSkillButtonClick()
    {
        SkillPannel._instance.ShowWindow();
    }
    public void OnSettingButtonClick()
    {
        SettingPannel._instance.ShowWindow();
    }

03 调用对应的面板的父类方法ShowWindow

public class Pannel : MonoBehaviour
{
    [Tooltip("父类Window的字段,不用设置,可调用而已")] public bool isShow = false;
    [Tooltip("关闭Pannel的按钮")] public UIButton closeButton;

    public void ShowWindow()
    {
        GetComponent<TweenPosition>().PlayForward();
        isShow = true;
    }
    public void DisableWindow()
    {
        GetComponent<TweenPosition>().PlayReverse();
        isShow = false;
    }
}

39-49 背包系统

TextAsset读取.txt

流程图

在这里插入图片描述

数据文本

1001,小瓶血药,icon-potion1,Potion,50,0,50,60
1002,大瓶血药,icon-potion2,Potion100,0,70,100
1003,蓝药,icon-potion3,Potion,100,60,80
2001,黄金甲,armor0-icon,Equip,0,50,0,Armor,Swordman,150,200
2002,铜甲,armor1-icon,Equip,0,39,0,Armor,Swordman,100,150
2003,神迹魔法衣,armor2-icon,Equip,0,50,0,Armor,Magician,150,200
2004,破旧魔法衣,armor3-icon,Equip,0,20,0,Armor,Magician,60,100
2005,铜鞋,icon-boot0,Equip,0,0,50,Shoe,Common,60,100
2006,神级红鞋,icon-boot0-01,Equip,0,0,70,Shoe,Common,120,150
2007,帽子,icon-helm,Equip,0,50,0,Headgear,Swordman,100,120
2008,神帽,icon-helm-01,Equip,0,70,0,Headgear,Swordman,120,200
2009,神级魔法帽,icon-helm-02,Equip,0,70,0,Headgear,Magician,120,200
2010,普通魔法帽,icon-helm-03,Equip,0,50,0,Headgear,Magician,100,120
2011,黄金戒指,icon-ring,Equip,0,50,0,Accessory,Common,50,70
2012,铜绿戒指,icon-ring-01,Equip,0,30,0,Accessory,Common,30,50
2013,盾牌,icon-shield,Equip,0,50,0,LeftHand,Common,50,70
2014,神盾,icon-shield1,Equip,0,70,0,LeftHand,Common,70,100
2015,皇族项链,icon-tailman,Equip,0,30,0,Accessory,Common,30,60
2016,火柴棍,rod-icon,Equip,40,0,0,RightHand,Magician,40,80
2017,金属棍,rod-icon02,Equip,60,0,0,RightHand,Magician,60,120
2018,神级魔法棒,rod-icon03,Equip,80,0,0,RightHand,Magician,80,200
2019,御剑,sword0-icon,Equip,40,0,0,RightHand,Swordman,40,60
2020,双手剑,sword0-icon00,Equip,60,0,0,RightHand,Swordman,60,100
2021,白帝圣剑,sword1-icon,Equip,80,0,0,RightHand,Swordman,80,150
2022,华夏第一剑,sword2-icon,Equip,100,0,0,RightHand,Swordman,150,200

(代码)模型类

没有MonoBehaviour,后面子类不能在编辑器内显示

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

public class Item:MonoBehaviour//物品信息类
{
    public int id;//000
    public new string name;
    public string icon_name;
    public ItemType itemType;
    public int price_sell;
    public int price_buy;//5
    public DressType dressType;//部位
    public ApplyType applyType;//职业
    public int attack;
    public int defense;
    public int speed;
    public int hp;
    public int mp;


    void Start()
    {

    }
}


public enum ItemType//物品的类型
{
    Potion,
    Equip,
    Mat,
    Undefined
}

(代码)TextAsset转化为List

两个方法的作用分别是
1、读TextAsset,写入Dictionary
2、读取Dictionary,返回一个上述模型类的一个对象

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

public class TextAssetToDictionary: MonoBehaviour
{
    [Tooltip("数据文本,英文逗号分割,回车换行")]
    public TextAsset textAsset;
    [Tooltip("组件不显示,是为了让访问调用")]
    public Dictionary<int, Item> dictionary = new Dictionary<int, Item>();

    public static TextAssetToDictionary  _instance;

	void Awake()
    {
        _instance = this;
        
        dictionary = Read(textAsset);
    }

    public Dictionary<int, Item> Read(TextAsset textAsset)//读取TextAsset,存储到Dictionary
    {
        string[] lineArray = textAsset.text.Split('\n');

        foreach (string line in lineArray)
        {
            string[] propertyArray = line.Split(',');//英文逗号
            //分流
            Item item = new Item();
            item.id = int.Parse(propertyArray[0]);
            item.name = propertyArray[1];
            item.icon_name = propertyArray[2];
            //根据类型在具体赋值
            switch (propertyArray[3])
            {
                case "Potion": item.itemType = ItemType.Potion; break;
                case "Equip": item.itemType = ItemType.Equip; break;
                case "Mat": item.itemType = ItemType.Mat; break;
                default: item.itemType = ItemType.Undefined; break;
            }
            //药品
            switch (item.itemType)
            {
                case ItemType.Potion://药品
                    {
                        item.hp = int.Parse(propertyArray[4]);
                        item.mp = int.Parse(propertyArray[5]);
                        item.price_sell = int.Parse(propertyArray[6]);
                        item.price_buy = int.Parse(propertyArray[7]);
                    }
                    break;
                case ItemType.Equip://装备
                    {
                        item.attack = int.Parse(propertyArray[4]);
                        item.defense = int.Parse(propertyArray[5]);
                        item.speed = int.Parse(propertyArray[6]);

                        switch (propertyArray[7])
                        {
                            case "Headgear": item.dressType = DressType.Headgear; break;
                            case "Armor": item.dressType = DressType.Armor; break;
                            case "LeftHand": item.dressType = DressType.LeftHand; break;
                            case "RightHand": item.dressType = DressType.RightHand; break;
                            case "Shoe": item.dressType = DressType.Shoe; break;
                            case "Accessory": item.dressType = DressType.Accessory; break;
                            default:  break;
                        }
                        switch (propertyArray[8])
                        {
                            case "Swordman": item.applyType = ApplyType.Swordman; break;
                            case "Magician": item.applyType = ApplyType.Magician; break;
                            case "Common": item.applyType = ApplyType.Common; break;
                            default:break;
                        }

                        item.price_sell = int.Parse(propertyArray[9]);
                        item.price_buy = int.Parse(propertyArray[10]);
                    }
                    break;
                default:
                    break;
            }
            dictionary.Add(item.id, item);
        }
        
        return dictionary;
    }

    public Item GetItemById(int id)
    {
        dictionary.TryGetValue(id, out Item item);

        return item;
    }
}




背包

流程图

ItemGroup就是一群,一堆Item的意思,多了数量的属性;
之前想用Bag_Item,但商店里面的也是一样的属性

实例的预制体是ItemGroup,继承于Item,Item的using… Start Update方法不能删除,否则它和子类都不能挂在(还是显示)在编辑器中
在这里插入图片描述

在这里插入图片描述

(问题)首位多次生成

之前记录的NGUI的有问题,就是拖拽最前面的有时(常常会生成物品)
因为是点击生成物品。拖第一个时形成空位,所以就生成物品在首位

for (int i = 0; i < gridList.Count; i++)
            {
                string itemName= itemList[itemIndex].GetComponent<UISprite>().spriteName;
                string goodName= gridList[i].childCount ==0 ? "" : gridList[i].GetChild(0).GetComponent<UISprite>().spriteName ;
                print(goodName+","+itemName);//测试
                bool isFind = (itemName==goodName);//新增的sprite名字在格子的子节点中是否能找到相同的

                if (isFind)
                {
                    print("相同");
                    IncreaseGood(i);
                    break;
                }

(代码)取数据,挂在BagPannel下

将字典的每一项item传给ItemGroup

  void InitItemPrefabList()//读取文本,派生Item预制体,生成对象
    {
        dictionary = GetComponent<TextAssetToDictionary>().dictionary;
        print("3423"+dictionary.Count);
        //遍历字典,填充itemPrefab,加入列表
        for (int i = 0; i < dictionary.Count; i++)
        {
            //看着文本的id来改
            Item item =new Item();
            if (i > 2)
                item = dictionary[2001 + i - 3];//药品
            //取值
            else
                item = dictionary[1001 + i];//装备

            GameObject itemPrefab = Instantiate(bag_Item);//生成
            itemPrefab.transform.parent = transform;//挂在总节点下,美观
            itemPrefab = itemPrefab.GetComponent<Bag_Item>().SetValue(item);//赋值
            itemPrefabList.Add(itemPrefab);//加入
        }
    }

(代码)ItemGroup

根据Item取值和UI显示,隐约觉得继承类可以用,但还没学到
继承Item的话会被Monobehavior排斥,导致无法在编辑器中使用添加。
所以还是将Item当成组件

(做到了),就是基类Item的using, start,update不能省略,不让没法拖到对象上
看到ScriptableObject,介绍单一的(视频是拖出一个,填值),暂时没学到它的价值


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

public class ItemGroup : Item
{
    //比Item额外多的
    [Tooltip("信息提示框")] public GameObject tipGo;
    [Tooltip("信息提示框的文本")] private UILabel tipLabel;
    [Tooltip("数量的文本框")] public UILabel countLabel;
    [Tooltip("数量")] public int count = 0;
    [Tooltip("信息提示框显示时间")] public float activeTime = 0.2f;
    [Tooltip("信息提示框计时器")] public float timer = 0f;
    

    
    void Start()
    {
            
    }
    void Update()
    {
            
    }

    public void DisableTip()//信息框计时隐藏
    {
              
        tipGo.SetActive(false);

    }
    public void ShowTip()//显示提示框文本
    {
        tipGo.SetActive(true);

        string tip = "";

        switch (itemType)
        {       
            case ItemType.Potion://药品
                {
                    tip = "名称:" + name;
                    tip += "\n类型:药品";
                    tip += "\n效果:Hp+" + hp;
                    tip += "\n效果:Mp+" + mp;                    
                    tip += "\n售价:" +price_sell;
                }
                break;
            case ItemType.Equip://装备
                {
                    string dressTypeStr = "";
                    switch (dressType)//部位
                    {
                        case DressType.Headgear:dressTypeStr = "头部";break;
                        case DressType.Armor:dressTypeStr = "身体";break;
                        case DressType.LeftHand:dressTypeStr = "左手";break;
                        case DressType.RightHand:dressTypeStr = "右手";break;
                        case DressType.Shoe:dressTypeStr = "脚";break;
                        case DressType.Accessory:dressTypeStr = "饰品";break;                    
                    }
                    string applyTypeStr = "";
                    switch (applyType)//职业
                    {
                        case ApplyType.Swordman: applyTypeStr = "战士"; break;
                        case ApplyType.Magician: applyTypeStr = "魔法师"; break;
                        case ApplyType.Common: applyTypeStr = "通用"; break;
                    }
                    //打印
                    tip = "名称:" + name;
                    tip += "\n类型:装备";
                    tip += "\n部位类型:"+ dressTypeStr;
                    tip += "\n职业类型:"+ applyTypeStr;
                    tip += "\n攻击:"+ attack;
                    tip += "\n防御:"+ defense;
                    tip += "\n速度:"+ speed;
                    tip += "\n售价:" + price_sell;
                }
                break;
            default:
                {
                    tip = "名称:未命名";
                    tip += "\n类型:未定义";
                }
                break;
        }
        tipLabel.text = tip;
    }
    public void SetValue(Item item)//父类的数据赋值给子类,因为数据是父类的
    {
        id=item.id;             
        name=item.name;
        icon_name=item.icon_name;
        itemType=item.itemType;
        price_sell= item.price_sell;
        price_buy= item.price_buy;
        dressType= item.dressType;
        applyType= item.applyType;
        attack= item.attack;
        defense= item.defense;
        speed = item.speed;
        hp= item.hp;
        mp= item.mp;
    }
}


(代码)拖拽组件

重写了UIDragAndDrop,加在物品上。
格子标签是Grid,物品预制体的标签是Good

using UnityEngine;

public class MyDragDrop : UIDragDropItem
{
    [Tooltip("开始拖拽的物体的位置")] private Vector3 startPos = Vector3.zero;

    protected override void OnDragDropStart()
    {
        base.OnDragDropStart();

        startPos = base.transform.position;//记录开始位置,为了回滚到原来的位置
    }
    protected override void OnDragDropRelease(GameObject surface)
    {
        base.OnDragDropRelease(surface);
        print(surface.tag);

        if (surface.tag == "Grid")
        {
            //transform.position = surface.transform.position;//物体放入格子里

            transform.parent = surface.transform;
            transform.localPosition = Vector3.zero;
        }
        else if (surface.tag == "Good")
        {
            //位置交换
            transform.position = surface.transform.position;
            surface.transform.position = startPos;
        }
        else//脱到不该脱的位置
        {
            transform.position = startPos;
        }
    }
}

(代码)增加物品,挂在BagPannel下

背包的总节点吧,方便做预制体

   void CreateNewItem(List<Transform> gridList,List<Item> itemList)//点击生成物品的测试函数,
    {
        //1 随机添加一个物品
        int createNewItemId = itemList[ Random.Range(0, itemList.Count) ].id;//新建的物品的id    

        //2 有没有相同的
        for (int i = 0; i < gridList.Count; i++)//找到图片名相同的grid的索引
        {
            if (gridList[i].transform.childCount == 0)//循环
            {
                AddNewItem(i, createNewItemId);//用拿到的id去对应的i实例
                break;
            }
            else if (gridList[i].transform.childCount > 0)
            {
                int itemGroupId = gridList[i].transform.GetChild(0).GetComponent<ItemGroup>().id;//取得格子里面的物品的id,moonobehavior排斥
                if (itemGroupId == createNewItemId)//相同,到对应的i加加
                {
                    AddExistingItem(i);
                    break;
                }
                else//循环
                {
                    if (i == gridList.Count - 1)
                        print("满了");
                }
            }
        }
    }
    void AddNewItem(int index,int id)//新增
    {
        Item item= TextAssetToList._instance.GetItemById(id);
        GameObject go = Instantiate(itemGroupPrefab);
        ItemGroup itemGroup = go.GetComponent<ItemGroup>();
        itemGroup.SetValue(item);
        go.GetComponent<UISprite>().spriteName = itemGroup.icon_name;
        go.transform.parent = gridList[index].transform;
        go.transform.localPosition = Vector3.zero;
        go.transform.localScale = Vector3.one;
      
       itemGroupList.Add(item);  
    }
    void AddExistingItem(int gridIndex)//旧增
    {
        Transform t = gridList[gridIndex].transform;//各自位置
        int count = int.Parse(t.GetChild(0).GetChild(0).GetComponent<UILabel>().text);//格子下的物品,物品下的Label对象
        t.GetChild(0).GetChild(0).GetComponent<UILabel>().text = (++count).ToString();
    }

(了解)UI Trigger组件

做鼠标悬浮的显示隐藏
over,out分别拖入显示隐藏信息提示框的方法
在这里插入图片描述

(问题)提示框中文不显示

支持中文的字体

(问题)图片过大

 go.transform.localScale = Vector3.one;

在这里插入图片描述

(问题)The root GameObject of the opened Prefab has been moved out of the Prefab …

现在只靠拖出来改,再拖回去
The root GameObject of the opened Prefab has been moved out of the Prefab …
在这里插入图片描述

50-52 状态系统(属性点数的分配、应用、重置修改)

流程图

在这里插入图片描述

UI

在这里插入图片描述

代码

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

public class Status : Window
{
    private int attack ;
    private int defense;
    private int speed;
    private int point;
    [Tooltip("数字部分的Label")] public UILabel attackLabel;
    [Tooltip("数字部分的Label")] public UILabel defenseLabel;
    [Tooltip("数字部分的Label")] public UILabel speedLabel;
    [Tooltip("数字部分的Label")] public UILabel pointLabel;

    // Start is called before the first frame update
    void Start()
    {
        GetValue();
        SetLabel();
    }

    //点击了相应的加按钮
    public void OnAttackClick()
    {
        bool havePoint = ConsumePoint();
        if (!havePoint) return;

        attack++;
        SetLabel();  
    }
    public void OnDefenseClick()
    {
        bool havePoint = ConsumePoint();
        if (!havePoint) return;

        defense++;
        SetLabel();
    }
    public void OnSpeedClick()
    {
        bool havePoint = ConsumePoint();
        if (!havePoint) return;

        speed++;
        SetLabel();
    }
    //够钱就扣,返回true;不然还钱,返回false
    private bool ConsumePoint()
    {
        point--;
        if (point < 0)
        {
            point++;
            return false;
        } 

        return true;
    }

    public void ResetValue ()//重置
    {
        GetValue();
        SetLabel();
    }
    public void SetValue()//应用修改
    {
        PlayerStatus._instance.attack=attack;
        PlayerStatus._instance.defense=defense;
        PlayerStatus._instance.speed=speed;
        PlayerStatus._instance.point=point;
    }

    public void GetValue()//到玩家那里获取数据
    {
        attack =  PlayerStatus._instance.attack;
        defense = PlayerStatus._instance.defense;
        speed =  PlayerStatus._instance.speed;
        point =  PlayerStatus._instance.point;
    }
    public void SetLabel()//输出到状态板的Label
    {
        attackLabel.text = attack.ToString();
        defenseLabel.text = defense.ToString();
        speedLabel.text = speed.ToString();
        pointLabel.text = point.ToString();
    }

}

问题 组件前面没有可选框

unity–为什么Inspector视图中脚本前面的勾选框没了
将一个父类方法的名字修改后,出现了TweenPosition丢失

背景是将 状态按钮 触发 父节点的方法 去调用 状态栏的父类方法ShowWindow
现尝试将 状态按钮 触发 父节点的方法 去调用 状态栏的父类方法ShowWindow,可以达到效果
所以问题在父节点的方法
在这里插入图片描述

53-55 商店系统

56-63 装备系统

视频是通过点击背包里面的装备来穿戴

想到,UI上
01 临时的Bag在左边,装备栏在右边,方便拖拽添加装备
02 界面不够显示两个面板,所以是,显示一个,隐藏一个,通过双击(右键)

需求:

01、打开装备栏点击部位,隐藏装备栏,弹出背包栏,显示玩家职业类型,该部位类型的人装备,再点击装备,实现穿戴或02、者替换,玩家属性的加减
打开背包,弹出背包栏,显示玩家职业类型,该部位类型的人装备,再点击装备,实现穿戴或者替换,玩家属性的加减
03、实现背包里面的数据和显示分离,方便显示可以装备的装备。存储的是

想到比Item就多了count属性,Group一群一堆。
框是做预制体用到的UI显示
在这里插入图片描述

流程

在这里插入图片描述

(重构)拆开数据表和对象表

背景是,在装备栏的部位槽点击进入背包,想背包显示的是该职业该部位属于装备的物品

想到,数据归一档,这个数据可以是所有类型的的物品表,装备类的表,药品类的表,装备老板的商品表,药品老板的商品表。

(代码)原来的

对应的,加减数量也需要更新背包,所以冗余了

    void AddNewItem(int index,int id)//新增
    {
        Item item= TextAssetToList._instance.GetItemById(id);

        GameObject go = Instantiate(itemGroupPrefab);
        ItemGroup itemGroup = go.GetComponent<ItemGroup>();
        itemGroup.SetValue(item);
        go.GetComponent<UISprite>().spriteName = itemGroup.icon_name;
        go.transform.parent = gridList[index].transform;
        go.transform.localPosition = Vector3.zero;
        go.transform.localScale = Vector3.one;
      
       itemGroupList.Add(item);  
    }

(代码)现在的,将显示部分拆出来复用


    void AddNewItem(int id)//新增
    { 
        
        Item item= TextAssetToList._instance.GetItemById(id);
        ItemGroup itemGroup = new ItemGroup();
        itemGroup.SetValue(item);
        itemGroup.count = 1;
 
       itemGroupList.Add(itemGroup);
    }

    void DisplayItem(List<ItemGroup> itemGroupList)//拿到对应的数据表 itemGroupList,进行实例
    {
        if (itemGroupObjectList!=null)
        {
            foreach (GameObject itemGroupObject in itemGroupObjectList)
            {
                Destroy(itemGroupObject);
            }
            itemGroupObjectList.Clear();//不clear,list里面有很多Missing的对象
        }
		重新展览
        for (int i = 0; i < itemGroupList.Count; i++)
        {
            GameObject go = Instantiate(itemGroupPrefab);

            ItemGroup itemGroup = go.GetComponent<ItemGroup>();
            itemGroup.SetValue(itemGroupList[i]);
            itemGroup.countLabel.text = itemGroup.count.ToString();
            go.GetComponent<UISprite>().spriteName =itemGroup.icon_name;
            
            go.transform.parent = gridList[i].transform;
            go.transform.localPosition = Vector3.zero;
            go.transform.localScale = Vector3.one;

            itemGroupObjectList.Add(go);
        }
    }

(问题) 在第一个格子死循环增加第一个随机出来的item

实例的代码在Display方法里面,Display裸露在Update,而不是在鼠标左键条件下。
也没有销毁之前的

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space) && _instance.isShow)//在背包打开的情况下点击
        {
            CreateNewItem(itemList);//克隆对象进背包
            DisplayItem(itemGroupList);
        }   
    }

再次Display,没有clear以前的对象列表

    void DisplayItem(List<ItemGroup> itemGroupList)//拿到对应的数据表 itemGroupList,进行实例
    {
        itemGroupObjectList.Clear();
        for (int i = 0; i < itemGroupList.Count; i++)
        {
            GameObject go = Instantiate(itemGroupPrefab);

            ItemGroup itemGroup = go.GetComponent<ItemGroup>();
            itemGroup.SetValue(itemGroupList[i]);
            itemGroup.countLabel.text = itemGroup.count.ToString();
            go.GetComponent<UISprite>().spriteName =itemGroup.icon_name;
            
            go.transform.parent = gridList[i].transform;
            go.transform.localPosition = Vector3.zero;
            go.transform.localScale = Vector3.one;

            itemGroupObjectList.Add(go);
        }
    }

点击装备槽显示对应职业、部位的装备(筛选)

    //从装备栏点击进来
    public void OnDressTypeClick(DressType dressType)//筛选出 职业类型,Item类型(装备),部位类型的物品
    {
        List<ItemGroup> equipGroupList = new List<ItemGroup>();
        foreach (ItemGroup itemGroup in BagPannel._instance.itemGroupList)
        {
            if (itemGroup.dressType == dressType &&
                itemGroup.applyType == Player._instance.applyType &&
                itemGroup.itemType == ItemType.Equip)//找出职业,部位符合的装备
            {
                equipGroupList.Add(itemGroup);
            }
        }
        BagPannel._instance.DisplayItem(equipGroupList);

        this.DisableWindow();
        BagPannel._instance.ShowWindow();
    }

左键装备,或者替换

    public void DressEquipItem(ItemGroup itemGroup)
    {
        //替换操作
        if (itemGroup.itemType != ItemType.Equip ) return;
        if (itemGroup.applyType != Player._instance.applyType && itemGroup.applyType != ApplyType.Common) return;
        

        Transform dressTrans = EquipPannel._instance.DressTypeToTransform(itemGroup.dressType);
        if (dressTrans.childCount != 0)//替换,多了回滚动作,其他一样
        {
            //卸下
            UnDressEquipItem(dressTrans.GetChild(0).GetComponent<EquipItem>());
        }

        MinusExistingItem(itemGroup.id);//减减 或者 销毁移除
        EquipPannel._instance.DressItemGroup(itemGroup);//装备
    }

物品预制体写方法传参id给BagPannel,被双击动作调用

    public void OnItemGroupDubleClick()
    {
        BagPannel._instance.UseItemGroup(id);
    }

在这里插入图片描述

脱下

    public bool UnDressEquipItem(EquipItem equipItem)
    {
        print("bag脱下");
        if (itemGroupList.Contains(equipItem))//有同类
        {
            AddExistingItem(equipItem.id);          
        }
        if (!itemGroupList.Contains(equipItem))//无同类
        {
            if (itemGroupObjectList.Count + 1 > gridList.Count)//满了,不能卸下
            {
                return false;
            }
            AddNewItem(equipItem.id);
        }

        return true;
    }
}

玩家双击EquipItem,向 装备栏 传递 穿戴类型

    public void UndressItemGroup()
    {
        EquipPannel._instance.UndressItemGroup(dressType);
    }

在这里插入图片描述

装备栏根据穿戴类型,销毁对应部位上的装备,回滚(加加或新增)装备到背包

  public void UndressItemGroup(DressType dressType)
    {
        //销毁,传id
        Transform equipTransform;
        switch (dressType)
        {
            case DressType.Headgear:
                equipTransform = headgear;
                break;
            case DressType.Armor: 
                equipTransform = anmor; 
                break;
            case DressType.LeftHand:
                equipTransform = leftHand;
                break;
            case DressType.RightHand:
                equipTransform = rightHand;
                break;
            case DressType.Shoe:
                equipTransform = shoe;
                break;
            case DressType.Accessory:
                equipTransform = accessory;
                break;
            default:
                throw new System.Exception("脱下装备出错");
            
        }
        //销毁回滚
        int id = equipTransform.GetChild(0).GetComponent<ItemGroup>().id;
        Destroy(equipTransform.GetChild(0).gameObject);
        BagPannel._instance.AddExistingItem(id);
    }

(问题) InvalidOperationException: Collection was modified; enumeration operation may not execute.

枚举列表时修改了列表

(问题) 文本提示框错误

给本体赋值,而不是给一个接收本体的对象赋值。错误文本框显示的是预制体填的值

                ItemGroup itemGroup = go.GetComponent<ItemGroup>();
                itemGroup.SetValue(item);
				//正确
				go.GetComponent<ItemGroup>().SetValue(item);

在这里插入图片描述

(现象) 设置字体的字距调整值"Miscrosoft YaHei"

拖个ttf的字体到unity,弹出这个很卡很久
在这里插入图片描述

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值