Paint in 3D

Paint in 3D用于在游戏内和编辑器里绘制所有物体。所有功能已经过深度优化,在WebGL、移动端、VR 以及更多平台用起来都非常好用!

它支持标准管线,以及 LWRP、HDRP 和 URP。通过使用GPU 加速,你的物体将以难以置信的速度被绘制。代码还经过深度优化来防止GC,和将所有绘制操作一起批次完成。

跟贴图系统不同,它是一个纹理绘制解决方案。这意味着你可以绘制你的物体上百万次,还是无帧率丢失,让你创作难以想象的游戏。

它在Unity应用商店上的售价是60美元,地址:https://assetstore.unity.com/packages/tools/painting/paint-in-3d-26286。

Photon

Photon中文翻译为“光子”,为有着15年服务器后端开发经验的德国Exit Games公司开发的高效,稳定,可拓展的网络引擎。为目前世界上用户最广泛,支持游戏类型最多的专业网络引擎之一,也是Unity应用商店里用户评价最高的网络组件。

世界多个知名游戏公司和工作室选用Photon作为其产品的网络支持引擎,其中包括WB华纳,Codemaster, 2K, Glu, 微软游戏工作室,史克威尔艾尼克斯,百代南梦宫,SandBox,雨神电竞等知名企业,也有许多工作室和新创企业正在了解和试用Photon之中。

它在Unity应用商店上有一个免费应用,地址:https://assetstore.unity.com/packages/tools/network/pun-2-free-119922。

当然,Photon需要注册账号、创建应用等操作才能使用,还不了解的同学可以去官方网站查阅相关资料。

温馨提示:Photon的国外服务器在国内使用比较卡,所以最好去中国官网申请国内的服务器,申请地址:https://vibrantlink.com/chinacloudapply/。

下面正式开始。

创建工程

使用Unity Hub创建一个3D项目,然后分别引入Paint in 3DPhoton Unity Networking 2,如下图:

温馨提示:在引入Photon Unity Networking 2后,记得配置AppId。

创建简易画板

为了方便演示,我们创建一个Quad作为画板,然后为其添加P3dPaintable、P3dMaterialCloner和P3dPaintableTexture组件,使用它们的默认配置即可,如下图:

然后,创建一个空的GameObject命名为OneMorePaint,然后向OneMorePaint添加P3dPaintSphere组件,修改P3dPaintSphere组件的Color为红色,其他配置保持默认不变,如下图:

再向OneMorePaint添加P3dHitScreen组件,勾选上P3dHitScreen组件的ConnectHits,其他配置保持默认不变,如下图:

这时候,创建简易画板就做好了,运行以后就可以画画了,如下图:

只不过,还是个单机版,我们加上实时在线功能。

连接PUN2服务器

创建一个C#脚本命名为Launcher,再创建一个空的GameObject命名为LauncherGameObject,把C#脚本Launcher添加到LauncherGameObject中。

编辑C#脚本Launcher为如下内容:

using Photon.Pun;
using Photon.Realtime;
using UnityEngine;namespace One.More
{public class Launcher : MonoBehaviourPunCallbacks{#region Private Fields/// <summary>/// This client's version number. Users are separated from each other by gameVersion (which allows you to make breaking changes)./// </summary>string gameVersion = "1";/// <summary>/// Keep track of the current process. Since connection is asynchronous and is based on several callbacks from Photon,/// we need to keep track of this to properly adjust the behavior when we receive call back by Photon./// Typically this is used for the OnConnectedToMaster() callback./// </summary>bool isConnecting;#endregionvoid Start(){this.Connect();}#region MonoBehaviourPunCallbacks Callbackspublic override void OnConnectedToMaster(){Debug.Log("PUN Basics Tutorial/Launcher: OnConnectedToMaster() was called by PUN");// we don't want to do anything if we are not attempting to join a room.// this case where isConnecting is false is typically when you lost or quit the game, when this level is loaded, OnConnectedToMaster will be called, in that case// we don't want to do anything.if (isConnecting){// #Critical: The first we try to do is to join a potential existing room. If there is, good, else, we'll be called back with OnJoinRandomFailed()PhotonNetwork.JoinRandomRoom();isConnecting = false;}}public override void OnDisconnected(DisconnectCause cause){Debug.LogWarningFormat("PUN Basics Tutorial/Launcher: OnDisconnected() was called by PUN with reason {0}", cause);isConnecting = false;}public override void OnJoinRandomFailed(short returnCode, string message){Debug.Log("PUN Basics Tutorial/Launcher:OnJoinRandomFailed() was called by PUN. No random room available, so we create one.\nCalling: PhotonNetwork.CreateRoom");// #Critical: we failed to join a random room, maybe none exists or they are all full. No worries, we create a new room.PhotonNetwork.CreateRoom(null, new RoomOptions());}public override void OnJoinedRoom(){Debug.Log("PUN Basics Tutorial/Launcher: OnJoinedRoom() called by PUN. Now this client is in a room.");}#endregion#region Public Methods/// <summary>/// Start the connection process./// - If already connected, we attempt joining a random room/// - if not yet connected, Connect this application instance to Photon Cloud Network/// </summary>public void Connect(){// we check if we are connected or not, we join if we are , else we initiate the connection to the server.if (PhotonNetwork.IsConnected){// #Critical we need at this point to attempt joining a Random Room. If it fails, we'll get notified in OnJoinRandomFailed() and we'll create one.PhotonNetwork.JoinRandomRoom();}else{// #Critical, we must first and foremost connect to Photon Online Server.isConnecting = PhotonNetwork.ConnectUsingSettings();PhotonNetwork.GameVersion = gameVersion;}}#endregion}
}

这时候,就可以连接到连接PUN2服务器了,运行以后我们可以看到如下日志:

实时在线同步

向之前创建的OneMorePaint添加PhotonView组件,使用默认配置即可,如下图:

创建一个C#脚本命名为OnlinePainting,把C#脚本OnlinePainting添加到OneMorePaint中。

编辑C#脚本OnlinePainting为如下内容:

using PaintIn3D;
using Photon.Pun;
using UnityEngine;namespace One.More
{public class OnlinePainting : MonoBehaviour, IHitPoint, IHitLine{private PhotonView photonView;private P3dPaintSphere paintSphere;void Start(){this.photonView = this.GetComponent<PhotonView>();this.paintSphere = this.GetComponent<P3dPaintSphere>();}public void HandleHitPoint(bool preview, int priority, float pressure, int seed, Vector3 position, Quaternion rotation){if (preview){return;}if (this.photonView == null){Debug.LogError("PhotonView is not found.");return;}this.photonView.RPC("HandleHitPointRpc", RpcTarget.Others, preview, priority, pressure, seed, position, rotation);}public void HandleHitLine(bool preview, int priority, float pressure, int seed, Vector3 position, Vector3 endPosition, Quaternion rotation, bool clip){if (preview){return;}if (this.photonView == null){Debug.LogError("PhotonView is not found.");return;}this.photonView.RPC("HandleHitLinetRpc", RpcTarget.Others, preview, priority, pressure, seed, position, endPosition, rotation, clip);}[PunRPC]public void HandleHitPointRpc(bool preview, int priority, float pressure, int seed, Vector3 position, Quaternion rotation){if (this.paintSphere == null){Debug.LogError("P3dPaintSphere is not found.");return;}this.paintSphere.HandleHitPoint(preview, priority, pressure, seed, position, rotation);}[PunRPC]public void HandleHitLinetRpc(bool preview, int priority, float pressure, int seed, Vector3 position, Vector3 endPosition, Quaternion rotation, bool clip){if (this.paintSphere == null){Debug.LogError("P3dPaintSphere is not found.");return;}this.paintSphere.HandleHitLine(preview, priority, pressure, seed, position, endPosition, rotation, clip);}}
}

在线涂鸦画板就制作完成了,我们看看运行效果怎么样?

运行效果

构建以后,同时启动两个客户端,效果如下:

当然,这只是简单的在线涂鸦画板,你还可以再此基础上添加更丰富的功能,比如:修改画笔颜色、修改画笔大小等等。

最后,谢谢你这么帅,还给我点赞关注

手把手带你使用Paint in 3D和Photon撸一个在线涂鸦画板相关推荐

  1. 【项目】手把手带你用 SpringBoot、Uniapp、MySql 开发一个简单的活动报名项目

    文章目录 一.前言和大致功能需求 二.用到的技术 (1) Java (2) MySQL (3) Uniapp 前端开发 (4) SSM 和 SpringBoot 三.数据库搭建 四.创建 Spring ...

  2. 全球顶会论文作者,28天手把手带你复现顶会论文

    作为AI从业者,怎样才有所建树,而不是浅尝辄止? 毫无疑问,当然是啃Paper.复现Paper呀! 对于本科生,论文复现可以帮你快速奠定理论基石并彻底搞懂,为课题研究打好基础: 对于硕博生,如果你要发 ...

  3. 手把手教你玩转CSS3 3D技术

    手把手教你玩转 CSS3 3D 技术 要玩转css3的3d,就必须了解几个词汇,便是透视(perspective).旋转(rotate)和移动(translate).透视即是以现实的视角来看屏幕上的2 ...

  4. 手把手带你爬天猫,获取杜蕾斯评论数据

    ↑ 关注 + 星标 ~ 有趣的不像个技术号 每晚九点,我们准时相约   大家好,我是黄同学 听说大家最近对爬虫感兴趣,所以今天手把手带你爬天猫. 爬虫爬什么呢? 因为海报出圈的杜蕾斯,真的是家喻户晓. ...

  5. 【手把手带你Godot游戏开发 第二弹】名场面临摹(教程目录 10月30日 更新)

    在[上一弹]中,通过对网红手游<FlappyBird>的临摹,我们初步了解了GDScript脚本语言以及Godot游戏引擎的基本工作流程,并且大家的脑海里应该已经建立起了一个可运转的游戏架 ...

  6. 飞浆PaddlePaddle-百度架构师手把手带你零基础实践深度学习(学习笔记)

    飞浆PaddlePaddle-百度架构师手把手带你零基础实践深度学习(学习笔记) 百度架构师手把手带你零基础实践深度学习(打卡学习笔记) 为什么学习飞浆PaddlePaddle,不得不说百度最近几年在 ...

  7. 3.1 计算机视觉的发展和卷积神经网络概要(百度架构师手把手带你零基础实践深度学习原版笔记系列)

    3.1 计算机视觉的发展和卷积神经网络(百度架构师手把手带你零基础实践深度学习原版笔记系列) 概要 计算机视觉作为一门让机器学会如何去"看"的科学学科,具体的说,就是让机器去识别摄 ...

  8. 百度官方论文复现营!顶会审稿人28天手把手带你复现顶会论文

    对于本科生,论文复现可以帮你快速奠定理论基石并彻底搞懂,为课题研究打好基础: 对于硕博生,如果你要发AI论文,就必须要快速大量的阅读特定方向的重要论文,并且理解创新点,进行复现,这才有利于找到自己的研 ...

  9. 老齐学python的django 源代码_《跟老齐学Python:Django实战》真正的手把手“带”您学习...

    全书通过不断升级改造的方式,带着读者完成了一个"极其"简单的实战项目.这里说简单是相对于那些"大牛"而言.如果您是大牛,齐老师会建议您使用谷歌或者读官方英文文档 ...

  10. 算法竞赛五冠五亚得主,手把手带你了解算法竞赛

    大家好,我是王贺(鱼遇雨欲语与余),一个工作两年的推荐广告算法工程师,热爱算法竞赛.今天跟大家聊聊我的新书<机器学习算法竞赛实战>,以及我的竞赛经历.明天晚上的直播我会以天池平台开放的二手 ...

最新文章

  1. Spring Aware 到底是个啥?
  2. java s字符_java 字符集s
  3. MyBatis(二)MyBatis基本流程源码分析
  4. RuntimeError: Expected to have finished reduction in the prior iteration before starting a new one.
  5. java中的祖先类_Java程序公共祖先类-Object
  6. matlab 动画_MATLAB作图实例:51:表面动画
  7. 【Vue2.0】—表单事件数据绑定(六)
  8. ubuntu下c++编译cpp(包含自定义类的多级调用)
  9. c语言程序功能实现不了,请高手帮忙看看我的程序,编译不报错,但是实现不了功能...
  10. vector.resize 与 vector.reserve的区别(转载)
  11. 读取tomcat下的文件夹路径
  12. 关于get和post的区别
  13. 深入理解JVM - 系统性能优化
  14. 使用ffmpeg合并多个视频文件
  15. [Python] 网络设备巡检脚本
  16. PMP之总价合同、成本补偿合同、工料合同
  17. Spring之IOC~控制反转
  18. Android获取设备序列号的方法与调用方式
  19. AltiumDesigner规则设置
  20. 从FutureTask内部类WaitNode深入浅出分析FutureTask实现原理

热门文章

  1. 485通讯测试软件,Modbus 测试工具| Modbus 通信测试软件
  2. Android Launcher负一屏实现方案
  3. 2018版USBASP烧录器改通用版教程
  4. 用python写脚本控制电脑息屏唤醒_Python制作小脚本,一键可以让你同事的电脑在你指定时间关机...
  5. hurst代码 python_python中的Hurst指数
  6. ipod nano 无法添加mp4视频 电影失败解决方法
  7. 固定效应和随机效应模型
  8. dz论坛php如何使用html模板,discuz模板制作 discuz怎样制作微信模板
  9. Java爬虫(三)后台发请求获取页面解析数据
  10. Centos7下安装Relion