AU Class
AU Class
class - AU

An Introduction to Scripting in Stingray (3ds Max Interactive)

이 강의 공유하기
동영상, 발표 자료 및 배포 자료에서 키워드 검색:

설명

AR/VR is a hot topic, and creating these kind of experiences require some knowledge of Real-Time engines. To go beyond simple templates and add interactivity, simulation and immersion, Stingray (3ds Max Interactive) offers different tools such as Flow, Stories and scripting. This last method is by far the most efficient and customizable, but can seem intimidating.

The session will be led by myself and facilitated by Paul Kind, who has been through the entire process of leaning scripting from scratch in Stingray (3ds Max Interactive).

This class is aimed at introducing scripting in Stingray (3ds Max Interactive)as a means to add interactions in your Immersive Experience and improve productivity. There is no pre-required knowledge of scripting, though a familiarity with Stingray (3ds Max Interactive) is required.

We start with the very basics, explaining concepts like variables, tables, and API calls, and move towards how scripting is integrated into Stingray (3ds Max Interactive) to create simple interactions via custom Flow nodes.

주요 학습

  • Learn how to write your first line of script in Stingray
  • Learn how to create a first custom flow node
  • Understand core scripting concepts through a concrete example
  • Discover a good organization structure to scale into larger projects

발표자

  • David Menard 님의 아바타
    David Menard
    David is a Software Engineer by training, with Master’s in Virtual Reality, and an MBA from HEC Montreal. After spending a few years working in the video games industry, he joined Autodesk to kick-start the effort around the ambitious project that is LIVE and Stingray, focusing on Virtual Reality. His deep experience in Virtual Reality and real-time rendering technologies has served him well as a Product Owner, enabling LIVE to become the One-Click solution to VR that it is today.
  • Paul Kind
    3D Artist and Game Developer who enjoys helping people come to grips with Stingray, Maya and Related Tools.
Video Player is loading.
Current Time 0:00
Duration 0:00
Loaded: 0%
Stream Type LIVE
Remaining Time 0:00
 
1x
  • Chapters
  • descriptions off, selected
  • subtitles off, selected
      Transcript

      DAVID MENARD: First question to all of you here. Who has never, ever written a line of code in their life? How many of you? OK. That's about five. And this is perfect. I'm going to just put this a bit lower. The class is really designed for a basic introduction for anyone who's never, ever scripted in their life. For everyone else, I hope you're going to find something here. Don't hesitate to ask questions along the way. I purposefully shortened the material of the class, so that we have enough time to chat, to ask questions. And hopefully the pace, I'll try and keep it very constant.

      So first things first. Before I even introduce myself, everyone just open 3dsMax Interactive or Stingray. You have Stingray? Click on Templates, and just double click on the Basic template. Create one, and we're going to let that compile while we do the rest. And I think everyone here is on trial. So you can just close that window, and it's going to compile.

      All right. So my name's David Menard. I work for Autodesk. I'm Product Owner for Revit LIVE. I joined Autodesk three years ago as a programmer. I went to school in programming four years, got a Bachelor of Engineering. This means I kind of have an unfair advantage, as well, here. Because obviously, I've spent four years studying to program. And Lua is all about programming, which is why I invited Paul with me-- where's Paul?

      PAUL KIND: I'm over here.

      DAVID MENARD: All right, Paul. Come up here. And Paul is kind of my success story. He's an artist-- well, I'll let Paul introduce himself. He's my success story. Win!

      PAUL KIND: Hello? Plug in? Remote?

      DAVID MENARD: Yeah.

      PAUL KIND: On. So I guess you guys might be wondering about Lua a little bit. And first I'm just going to start with who I am. So I'm an artist. I started with pretty much being an artist-- a 3D animator mostly, or 3D modeling. Basically game art. And that's where I started with.

      And I started using Stingray quite a bit. And I was actually on the Stingray team doing all the content for Stingray. And after very short order, I started working with Flow. And Flow took me to a really good place. And then I started to realize that I needed to do more than just what Flow can do-- not that Flow can't do it. But there's some times when you just want to make everything a little easier for yourself.

      And luckily, with Lua, you can do that very easily. And you can create your own Flow nodes. And I'm pretty sure that's what we're going to be learning here today is how to create your own Flow nodes. And once you learn that bit, you can start really accelerating what you do in Flow.

      So yeah, so I started in with Flow. But as an artist, I had no programming background-- or very, very little. I think I used Lingo, which was in Director from years prior. So yeah, if I was able to pick it up as an artist, I'm pretty sure that anybody can. It's not super complicated. And I would just say, take it one step at a time, learn slowly, and you guys are going to do great. But yeah, it's super awesome.

      So some of the things that I've done since then-- because now, it's like an addiction. Once you start making Flow nodes, you're like, oh, here, let's make more. So I have a whole library. It's available online, I think. Oh, we're not showing it. But if anybody is interested in downloading a library of already pre-built Flow nodes, just let me know, and I will give you the link to it. Or maybe David can put it up on screen.

      DAVID MENARD: Yep. Keep talking.

      PAUL KIND: OK. Yeah. So that's a repository on GitHub. You can download the whole package. And once you have the package, it gives you a whole bunch of new Flow nodes. And you can use that to figure out how to write your own Flow nodes. And I was actually super surprised, because I had never actually thought anyone was even touching them. And I've talked to so many people here at AU that have been like, oh, my god, I found your Flow nodes. They're amazing!

      So yeah, and it's super easy. It's really something anybody can pick up. And I really hope you guys do great with this class. And I hope that Lua works out well for you.

      DAVID MENARD: So by the way, the link is available in one of our downloads on the AU class. So let's talk a bit about how this is going to work. By the way, those who just joined us, just make sure you open Stingray, and you start a basic project, so that it compiles while we talk.

      So how it's going to work. First of all, I'm not going to present any videos. You know that usually, when you have hands-on labs or anything else, you prepare videos so that nothing goes wrong. The reason there is no videos, and the reason we didn't rehearse that much this class, is that when you're programming, you're going to make mistakes. It's completely inevitable that you're going to make mistakes.

      So today when I write the lab, or when I do it with you guys, I want to make mistakes. And if I'm not making mistakes, I'm going to make intentional mistakes, to show you how to find your mistakes, and how to debug those mistakes. And I've found that that's the hardest thing to be able to teach, when you're teaching programming, and when you're teaching new programmers. It's not writing the lines of codes. It's really finding your own mistakes, and finding what's wrong with it. So making mistakes is part of the lab.

      Next, we're going to actually follow the handout pretty line by line. So the handout is very detailed. We're not going to get to the end of the handout. We're only going to get about halfway through. If we have enough time, we might answer some questions about the rest. But by the end of the class, you should be able to do pretty much what Paul described-- writing Flow nodes, writing your own code, writing your own Lua functions inside of that. And writing really your own behavior inside of a Stingray or Max Interactive.

      So before we get going, I just want to introduce our lab assistants. Guys, just come up here. And they can introduce themselves.

      IGOR: Hello, everyone. My name is Igor. I am CTO of IM Designs. And I'm a nerd, and I like real-time rendering technologies. That's what we do.

      RAVI: My name is Ravi Ray Wood. I'm an Architect and BIM Manager with Charles Hilton Architects out of Greenwich, Connecticut. And we do luxury houses of like $100 million single houses. So thank you.

      DAVID MENARD: So thank you. Excellent. So these two guys are like Paul. They're self-taught as well. They started Lua with Max Interactive. So if you have any problems during the lab-- if you're stuck somewhere, if you have questions that are a bit more personal, just raise your hand really high, and yell out one of their names. Or yell out Lua or something. And they'll come and see you.

      And to our lab assistants-- if you see that there are too many questions at the same time, just tell me. Raise your hands, signal me, and I'll take a break. And maybe I'll go and try and help. All right? So we'll try and keep this as casual as we can. It's late in the AU, and we're all kind of tired. Hopefully everything goes well.

      All right. So let's get started. The first thing I want to mention is, throughout this entire lab, we're going to be building to something that's available in the online assets. Inside Scripts here, there's one thing called the Stingray App Kit Extensions. If you get to the end of the handout, you're going to end up with this. So this is pretty much a great example of where we want to get to. But we're not going to get to it by the end of the class. We're just going to get about halfway through.

      All right, let's get right into it. The first thing every single programming class gives you, or has you do, is the Hello World. So most of you are familiar with the Stingray project structure. There's this little folder called Scripts here. And in there, there's the Lua folder. There's a file called project.lua right here. If you double click on it, you're going to open this. And this is pretty much the entry point to every single project.

      Some of you have probably tried changing the default project. This is the default level that you're going to load. This is where you do it. But really, the first thing you want to do is at line 29-- or OK, the lines changed. But right here. We're going to write, print hello world, like this. And this-- congratulations. You've written your first line of code. So I think we've had five people who never wrote first line of code. Well, there you have it.

      So this is pretty simple. Obviously, now, whenever we click on Play, if we go in the log console-- and nothing works, of course. See? First mistake, and this was already unintentional. I put a little dash here. I'm just going to delete that. That's not compiling. So saving this again. And when you press on Play, basically when the project plays, you're going to see hello world in the console. Now it's just compiling again. So I'm going to wait just a second.

      While we wait, I'm going to give you a little pro tip. If you go in the window up here, there's a window called the external console. I almost exclusively use that to program in Stingray. The reason is that it has a debugger that I feel is a bit better than the default one. So in here, if you click on Lua, and open debugger, you're going to get a window in which you can open any project file, by just clicking Open. I'm just going to Open Project without Lua here.

      See this, Create Lua Project? Open this, and we're good to go. It's the same file. I can save from here. And we'll get into what the debugger does. By the way, did anyone else have this wonky character here? Or was it just me? It was just me? Everyone else compiled properly? I'm asking you.

      [INTERPOSING VOICES]

      OK. Very. Fair enough. And I saw [? Takahashi ?] just walked in. Where is [? Takahashi? ?] OK. We have another lab assistant, [? Takahashi, ?] right here. He's going to help us through the lab, as well. Thank you.

      AUDIENCE: Does anyone have a connector for the external [INAUDIBLE]?

      DAVID MENARD: Hm?

      AUDIENCE: Connector for the standard [INAUDIBLE]?

      DAVID MENARD: I didn't get that. All right, so this is almost compiling. By the way, every time you change something in the project, it has to recompile. The reason it didn't compile here for the first time when I loaded it is because I had a programming error. Obviously, it'll block the compiler at this point. So we just have to wait it out. Hopefully, everyone else is fine. And we can keep going.

      So in Lua, when you write a script, every single command that you write is going to be executed in a very linear fashion, right? You write print hello world, you write print hello world number two right after that. They're going to get executed sequentially. And there's really no magic to it.

      If something is happening in your experience-- in your game or anything that you write, basically, I am afraid to say it, but it's your fault. The computer won't do anything magical. It'll just follow your instructions by the book. And I mean very, very literally. And it'll surprise you sometimes how by the book it'll do things. I'm getting some echo now.

      So if I write, for example, print hello world, a second thing here-- second thing-- it'll just print them in the order. And caps are important. You see I put a capital P here. That would generate an error. I'm just going to change it to a small p. All right. I've finished compiling, I can finally run my game. And in the log console, we're going to see the print hello world.

      Oh. That's why. I was compiling for the Hololens. Of course.

      AUDIENCE: Under the compiler, how did you get this [INAUDIBLE]?

      DAVID MENARD: Simply click on Open, or Control-o. All right. So see? Hello World is being printed. Perfect. That's exactly what I wanted. All right.

      So obviously, Hello World is really genius. It's fun. We've written our first line of Lua. Yea. But now we want to actually do something that's interesting with the engine. Like the way you interact with the engine is not going to be by printing stuff. You want to be able to call the application programming interface, or API. So the engine API is just a way to interact, to call things that the engine can do, such as spawning units, moving units, detecting collisions-- that kind of thing.

      And your best friend when writing Lua scripts is the documentation. So under Help here, I want everyone to click on Documentation. Here. It's going to give you this page. And right here on the left, you have Lua API Reference. Your best friend is exactly this. You click there, and this page will detail everything you can do to interact with the engine.

      For our purpose, we're going to start with spawning a unit. That is done under-- actually, I can search here. Spawn unit. And you're going to see that I have World dot Spawn Unit. And we're going to use exactly this function. When you call these-- these are called functions. It's just ways you can interact with the engine. And you'll get used to this. We're going to come back to functions, and how to write your own.

      So the documentation says, I need to call Spawn Unit, and give a few arguments. These things are information arguments-- are information I need to give to the function, so that it knows what to do when spawning a new unit. Obviously, I need to give it a world, because this is under World here. Self is the world. I need to give it a unit name, so which unit to spawn. I need to give it a position, and then orientation.

      So let's get back to Stingray, or Max Interactive. By the way, I'm just going to say Stingray from now on. It's shorter. That's the reason. OK? And I'm going to go in my script, and at the same place that I printed this, my hello world, I'm going to spawn a unit.

      So the way you interact with the engine, or the API, the application programming interface, you always start with Stingray dot. So in our case, I was looking at the World API. So I'm going to go Stingray dot World dot Spawn Unit. So if I look at spawn unit, it says, Self. Self means just the world. So I'm going to give it a world. This-- we'll get back to what the app kit is, by the way. It's just app kit dot managed world, dot world.

      Then I'm going to give it a unit name. The unit name in this case is simply the path. And the relative path of the unit in your project. So I'm going to try and spawn my cube. My cube is under Content. And let's look at this. Content, Models, Props, and [INAUDIBLE] box. I'm going to copy this, because I'm never going to remember what it is. And I'm going to give it exactly this-- content, slash, models, slash, props, slash-- paste whatever I copied. Then I'm going to go back to my API, just to look at what I need to give it. Position orientation.

      And you see this little interrogation mark? The question mark here just says it's optional. So I don't actually need to give it a position or an orientation. If I don't, and if I read this properly, it's just going to spawn it at 0,0,0. Right? So I'm going to do that, and hope everything works well.

      So this is my preview. I moved this box, just to make sure that it's not in the way. Because if I spawn a box inside the box, I'll just think nothing happened. So I'm gonna click on Start, and-- was that my new box? That is my new box, spawned at 0,0,0. And you see, this is my other box. So there we go. We spawned a first box. We interacted with our engine for the first time.

      Now obviously, there is a Flow node that does this. This is not too interesting. But it is the first time we called an API, right? So let's say I don't want to spawn it at 0,0,0. If I go back to my documentation here, it says that the third argument I give it could be the position. Let's do that. Let's give it a position.

      So I'm going to do Stingray, Stingray dot vector 3. And I'm going to give it a position. Let's say 10, 10, 0. Stingray, if you remember correctly, is z up, right? So the up vector is in the z direction, or zed direction, if you're Canadian, like me. It's important. It's an internal fight.

      So next, when I plus on Play, the first thing that's going to be spawned is my cube, and at a different position. So yea, now I have full control over this. So anyone has a question up to now?

      AUDIENCE: Can you put the script back up?

      DAVID MENARD: Oh-- yeah. I'll put the script back up. There we go. I'm going to try and make this a bit bigger, too. And if your line is too long, you can simply index it. So you can write the same line on different lines. It won't do anything.

      AUDIENCE: Is that the product of [INAUDIBLE]?

      DAVID MENARD: Yes. So from I had the question earlier, how to open this file. When you're in the external console, you have to go into Lua open debugger. Once you're in the debugger, you simply click Open, and start writing stuff here to search for it. It's Script, Lua, Project. So I'm going to wait a second, just for everyone to make sure you catch up.

      So let's go back to our documentation for a second. This here-- the Spawn Unit function-- you'll notice at the end, it says, column Stingray.unit. This means it returns something. So when you spawn the unit, it will return a reference to that unit, so that you can use it later on. And we're going to store that in something called a variable that we can use later on.

      And a variable is really anything you can dream of. You call it what you want. You store whatever you want. And it's just something to help you store information and use it later on. So the way you declare a variable, you simply name it.

      In our case, we're going to use the Local keyword. That just means that the variable is only available inside this function. I'm going to call it, let's say my box is actually a chair, because we like chairs. So now, the unit that I spawned-- the box-- I called it a chair, because I think I'm going to use it as a chair later on. I can use this to later reference it, and maybe change its position. And let's actually do that.

      So how will I change its position? Well, now I have to go back to the documentation, because obviously, I don't know how to change a position of a unit. I think it's probably going to be inside Unit. So I'm going to open the Units documentation here, and probably going to search for something like set position. No. I'm going to search for position then. There you go-- local position or set local position. Perfect.

      So I'm going to use this function on my unit to be able to set its position some other time. And there is an i integer here, the index of the node. So my box, I only have one node in it. I think I only have one node in it. So let's try 1. So here, I'm going to do same thing-- Stingray.units.set_local_position. I'm going to give it my chair. Right? That's our first argument. If you look at the documentation, Self is always the kind of upstream API. So this chair.

      The index-- I said I'm going to try 1, because a node is always 1, in my case. And I'm going to give it a new position-- Stingray.Vector3. So do I have to explain what a Vector3 is? Or is everyone good? Does anyone not know what a Vector3 is? OK. Vector3. Perfect.

      A Vector3 is basically just three numbers in a row that you can use. Usually, they're used for positions. You can use them for rotations, as well. So in a game engine or a 3D world, you have three axes. Right? x, y, z. Well, Vector3s will describe positions on those three axes. Excellent.

      So a Vector3. I'm going to give it a new position-- let's say 20,20,20. So my cube is going to end up in the air. So now, if I press on Play-- where is my cube? It's up here. There you go. So that worked.

      All right. So we've seen with the API-- the application programming interface-- of the engine that we've been calling these things called functions. Right? Spawn Unit is a function. Set Local Position was a function. Now, let's build our own function. Functions essentially are just a set number of instructions that will get executed one after the other. And once you regroup those instructions together, you can reuse them as many times as you want.

      So let's say you want to set the position of a unit-- its position, its rotation, and its color. You can put that in a function, give it three arguments-- position, rotation, color. And then, just call that on a single unit many, many times. So it's really useful for organizing your code. And you'll see that they are crucial later on to creating your own Flow nodes.

      So let's just practice here and create a normal function that will do precisely exactly what we did. So in the same file, we're still in project.lua here. I'm going to call this function-- this is how you declare a function. Who knew? Function Spawn_A_Chair. You don't have to put underscores here, by the way. This is just kind of a programming style. A lot of people will do something like this. So I'll just do this.

      Then the function here, you can give it as many arguments as you want. So I'm going to give it the unit to spawn, and I guess a position. This seems fair. And to end the function-- to tell Stingray where the function ends, you just type end. So now, inside of this-- inside Spawn_A_Chair-- there's a set number of instructions that I can reuse. Right now there is nothing, but I can still call my function. So I can do something like this.

      Spawn a chair. Give it two arguments. So the first one, I said unit to spawn. I'm going to say this one. I'm just going to copy paste this, because I don't want to type all of this. And I'm going to give it a position, as well. Stingray.Vector3 just at 0,0,0. OK?

      So this is how you call a function. You just open and close the parentheses. Is this OK, by the way? Who's following me? OK. About half the class is following me. So we'll take a tiny one-minute break, just to make sure that people can catch on.

      Who has abandoned? Anyone abandon? OK. Do you want to try to catch up? Or are you just trying to follow?

      AUDIENCE: [INAUDIBLE]

      DAVID MENARD: OK. Because this happens a lot. Especially if you make a mistake at the beginning, it's hard to catch up.

      AUDIENCE: Yeah, you just want to wait [INAUDIBLE] You're way too fast.

      DAVID MENARD: Oh, OK. I'll slow down then.

      AUDIENCE: Quick side question.

      DAVID MENARD: Yeah.

      AUDIENCE: Is Lua the only scripting language that Stingray uses?

      DAVID MENARD: Yeah. So the question is, is Lua the only scripting language that Stingray uses? The answer is yes.

      AUDIENCE: [INAUDIBLE]

      DAVID MENARD: No. There is a C API, so C, C++ API, if you want to go that way. There are bindings between C and Lua, so you could implement your own scripting language. I don't think this is a subject we want to go into. [CHUCKLES]

      AUDIENCE: But we can use C, C++.

      DAVID MENARD: Yes. OK, so should I go back a bit? I just want to get some feedback.

      AUDIENCE: Yes.

      DAVID MENARD: Yes? On going back?

      AUDIENCE: Yes.

      DAVID MENARD: Back to where?

      AUDIENCE: [INAUDIBLE]

      DAVID MENARD: OK.

      AUDIENCE: What [INAUDIBLE]?

      DAVID MENARD: To spawning a chair, I guess. So if I go back here-- so remember, right. So if I explain variables again. So when you use the function-- the spawn_unit function-- I went to the API. There you go. And I'm just going to spawn, unit. So I obviously looked at the API and made sure that everything was here properly I think I should leave my code up a bit more, right? Does that make sense? OK.

      AUDIENCE: David?

      DAVID MENARD: Yep?

      AUDIENCE: [INAUDIBLE] many [INAUDIBLE] algorithm for the [INAUDIBLE] people who have bad eyesight. [INAUDIBLE]

      DAVID MENARD: There we go. So I'm going to delete my function. And let's make sure that everyone's following here.

      [INTERPOSING VOICES]

      AUDIENCE: Why would we be getting a nil value for this Flow?

      DAVID MENARD: Hm?

      AUDIENCE: You're getting nil value in manage world.

      DAVID MENARD: Manage world dot world?

      AUDIENCE: Yep. Manage world dot world. [INAUDIBLE] Thank you.

      DAVID MENARD: Yep. This?

      AUDIENCE: Yep. We're getting nil value.

      DAVID MENARD: OK. The question is that we're getting a nil value here. So this is causing an error. It's probably-- are you in the right project file? I'm just going to go back, and go and help.

      You're outside the project. You're outside the function. Put this in here. OK.

      So we had an error over there. I think it's a good example. The error was that we wrote the Hello World and all our functions up here. So if you're not in this function, the project dot on level pre_flow, these are not going to be available to you. So make sure you just write it under this line here.

      All right. So Dave Tyner's here as well to help. So don't hesitate to say hi to Dave Tyner. You saw him on the main screen yesterday. Now he's going to get red. [CHUCKLES]

      All right. Let's keep going. Let's rewrite our function. Right? How we write a function? We just simply write function over here, and call it spawn_a_chair. And then write end two lines later, just to make sure that Stingray knows where we're ending. And what I'm going to do, at this point, I'm going to just cut these two lines that I had under on project load pre_flow, and I'm going to pass them up here.

      And then, what I need to do to call this function-- I just need to open and close a parentheses. I'm just going to copy paste this, because it's safer. That's spawn-- yeah. And so this will actually call this function. So if I press on Play at this point, it's going do the exact same thing as it did before. It's going to spawn my unit. Actually, it's going to print "Hello World" first-- right here. It's going to print this second thing. Then it's going to spawn a chair.

      And by spawning a chair, it's just going to go up in here, to line 30, and execute these two lines of code. So it's going to spawn the unit and set its position. I'm going to show you what that does. There you go. So the unit is spawned, and it sets its position. I'm going to come back to the code, give you a chance.

      Is this pace a bit better, by the way, those who were still following? OK. Thanks.

      AUDIENCE: So question here?

      DAVID MENARD: Mm-hmm.

      AUDIENCE: Now you're writing the function above the function?

      DAVID MENARD: Yep. Yeah. We can talk a bit more about that later. There's something called scope that might be important. But it's not important here. Just make sure that if you-- yeah. You can write it above, and it's available down there.

      OK, so I think this is a good opportunity to show you the debugger. Remember I said that we're going to make mistakes? Unfortunately, I didn't make too many mistakes, except just at the start. Let's say instead of spawn the chair here, I would have made a typo. Called him spawn_a_shair.

      Now when I click on Play, I'm going to get some errors. You're going to see in the log console, it's going to say, "attempt to call global"-- can I? Hm. It's going to say up here, "attempt to call global spawn_a_shair a nil value." Usually when you see a nil value-- nil in programming, or in Lua, means nothing. It doesn't mean zero. It means nothing.

      So this basically says, I tried to call spawn_a_shair. But it's nothing. It means it doesn't exist. Obviously, because I called my function spawn_a_chair. So this is a mistake. So I'm just going to go ahead and look. If you actually look here, it's going to tell you at what line you made the mistake.

      So the debugger is friendly sometimes. In this case, it's very friendly. It's going to say line 42, I made a mistake. So right here, spawn_a_shair, I made a mistake. It didn't tell me exactly how to fix it. So I have to figure that out. But if I inspect the code a bit, I can see that I made a typo. So spawn_a_shair.

      Next, I want to know what's happening in my code. Let's say it doesn't behave the way I want. Let's say here it spawns at 10, 10, 20, and I don't know why. These little dots that I put on the side here-- they're called breakpoints. For this to work, make sure that you have the proper tab open here-- the one that's printing most of the text. So this one-- before opening the debugger.

      AUDIENCE: Question. If [INAUDIBLE] debugger?

      DAVID MENARD: In the external console, you mean?

      AUDIENCE: Right.

      DAVID MENARD: Yes. It's only in there that you can put breakpoints.

      AUDIENCE: OK.

      DAVID MENARD: You can put some as well in the script editor. So if I open Lua here, and double click on this, you can put breakpoints on somewhere here. Or maybe they removed that. But to be honest, the external debugger is a lot better. So I would just recommend you use that.

      AUDIENCE: Yeah, how do you get [INAUDIBLE]?

      DAVID MENARD: To this?

      AUDIENCE: Yeah. Just [INAUDIBLE].

      DAVID MENARD: OK. Under Window, there is a external console. This is it.

      AUDIENCE: How do you open the player [INAUDIBLE]

      DAVID MENARD: So what was the question?

      AUDIENCE: In that console, I don't see anything that says, open.

      DAVID MENARD: OK. In this? It's under Lua-- Lua Debugger.

      AUDIENCE: Thank you.

      DAVID MENARD: And you'll see this is a console. It's basically writing the same stuff that you have in here. And the debugger lets you inspect your code. All right.

      So once I have this, if I put a breakpoint, you're going to see that, whenever I hit Play in my game-- now it's all black. The reason it's all black is because my breakpoint was hit, and no code is being executed at all. OK? So it just stopped right here. And it didn't even execute this line of code. You can see in the console, there's no Hello World. In my external console, there's still no Hello World. So nothing has been executed.

      And from here, you can step line by line to see exactly what the code does. So if I press Step Forward or Step Over-- the shortcut is also F10-- you can simply see, line by line, exactly what the code does. Hello World. Then when I press F10 once again, or step over, it just went, Second Thing. Then when I get to a function, I can either step over-- which will go to the next line of code right here-- or I can step into. And that's going to go inside the function.

      Step into. Then you're going to see, I get to the line where I spawn my unit. And even if I press F10 here, now that this line has been executed, there's a chair in my variable called chair. You can even see it down here. There's a unit in it. But you still can't see anything.

      The reason you can't see anything is because this is an interactive engine. It needs to update 30 times a second, or 90 times a second if you're in VR, or enough times a second so that it's interactive. If I paused it here, the world has a unit in it, but it hasn't been drawn yet, not even once. So for me to draw it once, I actually have to let it go.

      So from here, I debugged what I needed. So I can press F5. F5 will simply tell the debugger, keep going until you hit a next breakpoint. And I don't have another breakpoint. So at this point, everything's fine. And I can stay interactive again.

      AUDIENCE: Sorry. Do you have a breakpoint [INAUDIBLE] it's not stopping.

      DAVID MENARD: OK, when it's not stopping, it's probably because a debugger was not opened with the right console window. Let me show you. Then I'll show you on screen after. So close this.

      Has anyone been able to hit a breakpoint, by the way? I'm thinking there might be something with the VMs. Yeah?

      AUDIENCE: I had to re-run it twice. [INAUDIBLE] that it finally went through.

      DAVID MENARD: OK. That's kind of weird.

      AUDIENCE: [INAUDIBLE]

      DAVID MENARD: [INAUDIBLE]. So usually, with these, you just want to make sure you have the tab that's printing text whenever you press on Play. So let's say I press on Play. You see, there's text that's been printed here. I want to make sure I select this tab before launching my Lua debugger. And there's a good reason for this.

      The reason for this is that I can debug more than one engine at the same time. I can actually debug a Hololens, if I have one connected, an Android device, iOS device. I can even debug the code that's running in the viewport here if I want to. But you need to have the right tab selected. If it's not working properly for you, try running the game instead of playing the level. The game will actually spawn another tab, and then you can hit the debugger, and it should still hit the same breakpoint. Let me know if that works. Cool. So that worked.

      I'm going to repeat this. If you're having trouble attaching the debugger or hitting your breakpoints, just click on Run Game instead of Test Level. Run Game is going to start a new tab up here. You can see that's the one that is going to print text whenever I launch the game. And from there, I can open my debugger. And then my breakpoints will be hit.

      All right. Are we good to keep going? Anyone want me to repeat something? Questions?

      AUDIENCE: Can you scroll up a bit?

      DAVID MENARD: Yep.

      All right. Next we're going to jump. We have functions pretty down right now. We know how to call the engine API. Next, we're going to create our first Flow node. These are super useful, because they can encompass sets of functions that either don't exist in the current Flow graph, or a set of functionality or behaviors that you want to encompass or keep organized in your project.

      A great example, as Paul mentioned earlier, he made a little laser teleport-- that's one Flow node. He wrote the code for that in about 20 lines, put that in a node, and now he can use it in the Flow graph as he wants. So writing Flow nodes is very similar to the same thing. We're going to have to create a function. But first, we're going to have to define our Flow node-- what it's going to look like, how it's going to appear in the Flow graph.

      So once again, I'm going to go into my documentation. If I go back to the Help Home, and I search for custom flow-- let's search for custom flow. There's going to be a page somewhere here-- Create custom Flow nodes in Lua. This page describes really well the procedure to create custom Flow nodes. It will also describe some more advanced concepts for creating Flow nodes. I usually always have this page open when I create my own Flow nodes, because I just keep forgetting the concepts.

      But for now, we're going to keep it simple. And we're going to open a file in the Scripts folder. In the Scripts folder here, there's a file called Global. And if I open this folder in Explorer, this is actually a dot script flow nodes. So guess what? All your Flow nodes will be described in here.

      You can open it in the editor of your choice or the Stingray editor. I'm going to open it in the Stingray editor. Hopefully I can zoom in here. I can't. So I'm going to open it in another editor, and hopefully I can zoom in here.

      So when you open a template project, there's usually at least one template node. Under nodes, you can see the bracket opens here and closes here. There's another bracket that opens here, closes here. So everything inside of this here is one node. So if I go in my Level Flow, and I look at this Example Print node, the category is "Project," and the name is "Example Print."

      So if I go in Project in here, there is an Example Print node. And this is actually a custom node for this specific project. So we're going to do something very similar to this node right here. So first thing I'm going to do is, I'm going to copy paste this, because learning by example is always great. And I'm just going to copy paste the exact same thing right under it. So [CLICK] right there.

      I'm going to call this "Open URL In Browser." And guess what this Flow node, what we're going to make it do. It's just going to pop any URL you give it into a browser page. Something that doesn't exist in Flow today, something that the engine provides through its API. But we want to expose it to Flow, so that we can add custom events to it.

      The args part of your Flow node is what the Flow node will take in. Remember earlier, we had functions, and the functions took different arguments? The spawn unit function, for example, took-- what was it? A world? A position and a rotation. This is the same thing. This Flow node here-- we will tell it that it needs to take a URL, and that is a string. A string is just a series of characters, if you're wondering.

      So ironically, here, right here, "string" is a string. The Category is where the Flow node will appear when I right click here. So if the Category is Animation, it will appear under Animation. If it's under Project, it will appear under Project. You can see that mine just appeared here, "Open URL In Browser."

      You can create your own category, too. So I'm going to call this "DavidNodes."

      AUDIENCE: Hey, David?

      DAVID MENARD: Yep?

      AUDIENCE: Why did you capitalize URL instead of [INAUDIBLE]?

      DAVID MENARD: There is no good reason for that.

      AUDIENCE: All right. But it matters?

      DAVID MENARD: Yes, it matters. Capitalization matters. So the question was, why did I capitalize URL? I had no good reason to do that. It's just a personal choice. It does matter, though. If I capitalize it there, when I use it later on, it has to be capitalized. So capitalization matters.

      AUDIENCE: So you left it in lower case when you type it in, because you have to take lower case.

      DAVID MENARD: When you use it in a function, in the code, later on? Yes. Not when you type in the URL, though. That doesn't matter. Yes. Exactly. And I'll show it to you later. Here. I'll actually do this. So that it's stupid, and looks bad, and is prone to errors. This will make you go nuts.

      OK. The last thing is the function here. And this is the most important one. Whenever you call this Flow node, this is the code that's going to be executed for this Flow node. Now here, it says, "ProjectFlowCallbacks.example." We're going to change this to "ProjectFlowCallbacks.open_url_in_browser."

      And I'm going to stop there for now and go to my Level Flow. Is everyone good with this? So if I go to my Level Flow, what I want to do is, I want to call this whenever I press Space bar. So I'm going to first create a Flow node for keyboard button.

      To search your Flow nodes like this, by the way-- my favorite function ever-- you just press on Tab. Press on Tab, then you can write pretty much anything, and it's going to find it. So if I want to spawn unit on position, I can look for stuff.

      AUDIENCE: Does that work in the Unit Flow now?

      DAVID MENARD: No, not yet.

      AUDIENCE: No?

      DAVID MENARD: No. I know. I'm so sorry. [CHUCKLES] All right, so the button name here, I'm going to just search for space. Going to Save. And now I'm going to write my custom node. I put it under David's Nodes, right? Yes. "Open URL In Browser." So whenever I press Spacebar, I want to open this URL. I'm going to say, www.Google.com.

      And I'm going to press Play with this. Leave it there for a second.

      AUDIENCE: Would this be a reason why a node wouldn't show up?

      AUDIENCE: Yeah. Not getting an error.

      [INTERPOSING VOICES]

      AUDIENCE: I've got the casual read shows that open URLs, there's an option to add. And then, when I click that, there's nothing.

      DAVID MENARD: No reason for that.

      AUDIENCE: There it's copied and pasted.

      DAVID MENARD: We're getting [INAUDIBLE]

      OK, a lot of people are having problems with the Flow node not appearing when you click on it.

      AUDIENCE: [INAUDIBLE] keyboard [INAUDIBLE].

      DAVID MENARD: I'm going to answer that question in just a second. A lot of people are getting problems that, when you click on the node here, it just doesn't appear. It doesn't show up. I think it's a problem with the VMs. For some reason, the connection is not happening. Just restart the editor, and it seems to work after that. Are you getting the same thing, Dave?

      AUDIENCE: Yeah, we restarted it.

      DAVID MENARD: OK. And it seems to work fine.

      AUDIENCE: Show it works on the [INAUDIBLE].

      DAVID MENARD: The next question was, how do you get the Keyboard Button node? You can either search for it, under Input-- somewhere in here-- Keyboard Button. Or my preferred method is you press on Tab, and then you can search for nodes. So I simply search for keyboard, and Keyboard Button shows up. And from here you can select your button. We have another question over here? Or same thing?

      Excellent error. Perfect.

      AUDIENCE: Got a good one, then.

      DAVID MENARD: We had an error that said, "Script Flow node definition not found." Is because the name in the definition of the Flow node was not found. It was capitalized. I'll show it to you. Just quickly, in the definition of the Flow node, we had someone go like this. And I don't want to point fingers, because I've done it many, many times.

      This, whenever you go in the log console, you'll see that it gives you errors. Or even here. So it's giving me, "Found invalid script Flow node definition." That's because it didn't recognize name. So a property name must be a string. So here, we just need to make sure it's lowercase, not uppercase. All right? So does anyone not have this at this point? OK. We'll wait for a second.

      Next thing we want to do is, we want to go find wherever this function is defined. Right? If we click on Play at this point-- I'll click on Play. And I press on Space. This is going to generate me an error. Right? There we go. And the error is called "Function not found. ProjectFlowCallback.open_url_in_browser." This is a very friendly error. It basically tells you exactly what's going on. Function not found. It can't find this function. It's because either it doesn't exist, like in our case, either it has a typo in it, or either there's some other thing that's going wrong. But usually the two main reasons this happened is it doesn't exist or there's a typo. Usually when this happens, you have a typo.

      All right? So we're going to go create this function in our scripts. So if I go in Scripts in Lua, there's a script called Flow callbacks here. I'm going to open that in my debugger. So again, just checking in. This is a better pace than at the beginning? Most people are following? Thanks. Excellent.

      All right. So once this is done, we can see we have the ProjectFlowCallbacks.example function that's defined here. And this is a function that the example Flow node was calling. Now we want to define another one. ProjectFlowCallbacks.open_url_in_browser. So what I'm going to do-- simple enough-- I'm going to create a function called Project, Flow, Callbacks. And if you want to avoid retyping, just copy paste. Again, just avoids errors. Dot open_url_in_browser.

      So one thing to note about Flow nodes, or Flow node function callbacks. These functions that you're defining will always, always take one argument right here. And I call it t. It's called t by default. I don't know if, Dave, you have other experience than t?

      AUDIENCE: [INAUDIBLE]

      DAVID MENARD: Just a long thing to be annoying. OK, call it t. t is for table. [LAUGHS] So we didn't talk about tables just yet. I skipped that part earlier. We're going to get back to it. But t contains every other argument that we sent into our Flow node. So here, we simply defined one argument called url. So to access that, I can do t.url.

      And remember, I did something very stupid here, and I capped the R, just to be annoying. So if I don't do this, it's going to fail. So I'm just going to change that. Because it's really bugging me. There you go. No. I pressed on Control-S to save, and now it's telling me I have a compile error. So it says, "The following resource failed to compile." This one. "Error compiling line 13 near end." This error happened because this is not a valid Lua line. So as soon as you write something that's not valid, and save, it's going to try and compile for you. If it's not valid, it's going to fail right away.

      So I have to do something with the URL, right? But before doing anything, I'm going to print. I'm going to print the URL-- t.url. Just to make sure it works. Because you want to take tiny, tiny steps when scripting, and execute as much as you can that way. You catch errors early on.

      And you'll see, eventually, you get the right reflexes writing your nodes, writing your scripts, when it comes to API calls. But you never, ever get the right reflexes when it comes to human logic. You're always going to make errors. And it's always going to be stupid errors. And we've all been there. So the earlier you can catch them, the better. That's why you need to execute your scripts as often as you can.

      So now, I'm going to go back. I'm going to press on Play I'm going to look in my console, just to make sure that stuff is happening. The Hello World and the Second Thing are still being printed. I'm going to press on Space, and it's printing nil. That is because I didn't not give it a URL. So I'm going to give it something.

      So already, I caught this error just because I executed it. If you remember, early on, I actually typed in Google.com. You remember me doing that? The reason it disappeared is because I changed the name of my variable. I changed it from a higher cap to a lower cap. So it just erased it, because it didn't recognize it. So that's what happened here. See? Mistakes. All the time.

      I'm just going to re-execute this to make sure I have everything in place. I'm going to press Space bar a few times. And there you go. It's printing Google.com. So now I know I'm all set to keep going here. And I'm just going to replace this script, to make sure that everyone is fine with this. Any questions while we wait? Yes.

      OK. So we're going to keep going, and we're going to make sure that this actually opens a URL in the browser. Lucky for us, if I go back to my documentation, in my Lua API Reference, once again, I will search for open-- there you go. Application open_url_in_browser. I'm going to click on this, and I'm going to look at what it does.

      So we're under Application. So we're going to start with Stingray, dot, application. And the function is called open_url_in_browser. So I'm going to write Stingray, dot, application, dot open_url_in_browser. And it takes in one URL, and it returns nothing. This function does not return any values. So I'm going to do exactly that. Instead of-- I'm going to write here, stingray.application.open_url_in_browser.

      And I'm going to give it my URL-- t.url. So fingers crossed I did everything correctly. Going to leave this here for a sec. Let's press on Play. And if I press on Space, I got an error. Perfect. What's my error? So this is a long error. When you have a very long error like this, you want to always look at the start-- the top one. So it printed my URL correctly. Then it says, URL must be HTTP or HTTPS protocol. So my guess from this is that I need to add HTTP in front of this. Something like this.

      So everyone here is probably going to get the same error if they went with Google.com. So make sure you write HTTP, colon, slash, slash. I like to clear this just before executing-- keep it clean. You can press on Play, and now, when I press on Space bar, it actually opens the URL in the browser.

      AUDIENCE: Woo!

      DAVID MENARD: We got it! We got a woo. [CHUCKLES] OK. We're going to take--

      AUDIENCE: Dave?

      DAVID MENARD: Yeah?

      AUDIENCE: So in here, is it just a coincidence that you named the function in the ProjectFlowCallbacks open_url_in_browser and also, the API call to do that is the same name?

      DAVID MENARD: Yes. It's a pure coincidence.

      AUDIENCE: OK.

      DAVID MENARD: Just to illustrate this, I'll just do this. OK? And this will still work. Remember, if I change it here, though, I have to change the function that it calls in my definition. Right? So open_browser. So this was a pure coincidence.

      AUDIENCE: OK. And a more question, David. How does anybody know to look-- how does it know that it's going to be this-- how does it know that your function is going to be in your project program callbacks? Is there another spot that it doesn't have to look here, [INAUDIBLE]?

      DAVID MENARD: So the question was, how does Stingray know to look in the Project Flow Callbacks table. Right? So the Project Flow Callback table-- OK, let's do tables first, before answering that question. To answer the question really rapidly, the reason it knows is because it's looking in the global namespace. And this happens to be in the global namespace. Yes.

      AUDIENCE: [INAUDIBLE]

      DAVID MENARD: Oh. Where are my lab assistants? They left? [INAUDIBLE] they left. [CHUCKLES]

      So we're going to explain tables a tiny bit before moving on. So tables are just variables. They're containers that can contain more than one object. When you declare a variable, you can only put one thing in it. A table can contain a series of things. So it's very useful if you want to declare 100 units, and spawn 100 units, to put those in a table, so that you can iterate-- so go through them one by one-- and do something on them one by one.

      Tables are a series of key and value pairs. So the way you look up something in a table is through a key. The way you insert stuff is with a value. We'll do something concrete here. So remember how you declare a variable? Local. And I'll declare a table. You just open the curly brackets and close it. And this is an empty table. And I'm going to call it my_table, just to make sure that there's no confusion here.

      Then I want to put stuff in my table. So we had this function that we created earlier called spawn_a_chair. It didn't return anything. But here we actually created a chair. So we're going to return the chair, because the functions can return something. And the way you do this is simply return chair. So now, whenever we call this function, what it returns we can store it in a variable.

      So let's do that. Local chair1 equals spawn_a_chair. I'm gonna call chair2. And we only have 15 minutes left, right? So now in my table at index 1-- and this 1 here is actually my key. So when I want to look up something in my table later, I can use 1. This can be anything. It could be a string if you want. But at the index 1, I'm going to put chair1. And my_table, at the index 2, I'm going to put chair2.

      So now notice, I put a breakpoint here, just because I want to see what's going to be in my table. You notice there's also these green lines here. These are called comments. It means these things are not being executed. Comments are great to leave notes to yourself for the next day, because we're all human. We all forget what we did the day before. Yesterday was a prime example.

      So comments are really great to either explain an obscure part of logic or to leave reminders of stuff that you did. Multi-line comments-- you can add comments on more than one line-- are done like this. And then you close it like this. So at this point, my_table-- and I like doing this for myself. I like drawing what my table will actually contain. And this is not actual code. It's just to give me a sense of what I put in my table.

      There's going to be a chair1, and there's going to be a chair2. So I expect my table to look like this after this series is executed. And you don't need to follow along with this, because this doesn't actually do anything. I just want to explain the concept a bit.

      So when I press on Play, I think I had the right debugger opened. Apparently not. I'm going to close this. Going to make sure I have the right tab opened. Then open my profiler. And it did not. Apparently it was not the right tab because I had two running. So notice, I put a breakpoint on an empty line. That might be why it was not reaching this. That's always a bad habit. Can we try again?

      There we go. It hit my breakpoint. All right. So here, if I execute, I jump to the next line. Down here-- I wonder if I can zoom-- OK. Down here-- I hope it's big enough for you guys-- I can see I can expect what's in my table. So this is really useful, especially when you have a lot of data that you want to put in tables. You can actually see what you have in there. So if you want to put values, you can inspect the indices, you can check that the values are correct. You can check everything that's in there.

      And the tables-- this is actually the exact same structure that was used when we declared our function here-- the ProjectFlowCallbacks. So this is a table, and it contains the URL. So I may put a breakpoint here, and press on F5. So if I press on Space bar, you can see, I can actually debug my Flow callbacks. I just put a breakpoint, pressed on Space bar, and the t-- you can see what's contained in here, the URL. So I can make sure the URL is correct if there's a problem there.

      If there's more than one thing in t-- say I declare. In the arguments, you can put anything here. You can put a value and you'd call it a float. Well then, your table would contain a URL and a value. It's that simple. And then your Level Flow would take a second in thing. Where's my? Mm. I changed the name.

      So we only have about 10 minutes left. So I want to open the floor to questions. At this point, we've written our own Flow nodes, right, to open the URL in the browser. You can write a Flow node maybe to change the position of a unit. We've changed it in the project.lua. You can say that, whenever I press on Left, you change the position of a unit. You can add to these one after the other to build up your Flow node library, and really build up the kind of thing that Stingray can do.

      The handout goes through this line by line, and explains every single concept with a small paragraph. We're about halfway through the handout at this point. Like I mentioned, I did not expect that we were going to go through the entire handout. That would be very ambitious. The concepts after this point start being advanced. OK? We're looking at classes, core routines, all that stuff.

      But most of what you can do in Stingray-- and most of what you will want to do in Stingray-- can be achieved with this. As long as you find the right function in the Stingray API, you'll be able to interact with the engine in the way that you want.

      AUDIENCE: How do we know what the engine can do, so that we know [INAUDIBLE]?

      DAVID MENARD: Ah, that's an excellent question. And there's no good answer. OK, the question was, how do we know what the engine can do, so that we know how to look for it? Most of what you can think of, someone has implemented it in the engine. So it's just a matter of searching for it. You try keywords.

      It's very similar in all engines. Some engines have deeper APIs than others. It's a matter of searching the documentation. And you'll see that eventually you find some keywords that come back frequently. So if I'm looking for stuff related to positions-- local, world-- you type in position, and you get a list of 50 things that it can do related to positions. You just scan through them. And eventually, you kind of get an idea.

      I started with Unity, and lots and lots of stuff that you can do with Unity. It's the same process.

      AUDIENCE: Are you able to expose your function to the inspector after the UI level? So if I was working with the team, and I was writing some scripts for that, would they be able to change the color by exposing my function? Or is that something that you have to commit the Flow?

      DAVID MENARD: So the question is-- let me make sure I understand correctly-- are you able to expose the function to other programmers so that they can use it?

      AUDIENCE: No. Sorry. [INAUDIBLE]

      DAVID MENARD: Oh, to the Property Editor.

      AUDIENCE: Right.

      DAVID MENARD: Usually you want to go through Flow in that case. It's something that will come in. You can also use-- we used Level Flow at this point. There's also Unit Flow. So Flow nodes that are specific to one unit, and Flow graphs that are specific to one unit, you can expose those kinds of parameters in the unit. The unit in Stingray is kind of this same thing as a game object in Unity. So there is external in events. There's something like that. Maybe Paul knows. Go see Paul. I'm sure he knows.

      AUDIENCE: Thanks.

      DAVID MENARD: Yes?

      AUDIENCE: So the table could just be declared in [INAUDIBLE] that why [INAUDIBLE]

      DAVID MENARD: You mean the ProjectFlowCallbacks?

      AUDIENCE: Right.

      DAVID MENARD: Yes.

      AUDIENCE: And then it says [INAUDIBLE].

      DAVID MENARD: The first line is actually a nice little trick that Lua lets you do. Lua, by the way-- super lightweight, super powerful, super fast. It lets you do some neat tricks like this. That first line means, declare a variable called ProjectFlowCallbacks. And if it already exists, just set it to ProjectFlowCallbacks-- the one that already exists. Or if it doesn't exist, just empty table.

      So the first time this script is ran, because all scripts are ran-- you run scripts-- it's going to declare a new variable. If you happened by mistake to run this a second time, it's just not going to do anything. So that's a nifty trick. You'll see this happen a few times in Lua. And you get used to these tricks.

      AUDIENCE: OK.

      AUDIENCE: Another question is--

      DAVID MENARD: Yeah?

      AUDIENCE: Just want to make sure I'm [INAUDIBLE]. Another question-- is there a central location where people have been placing their available Flow nodes, that we're able to have access to things?

      DAVID MENARD: OK, so the question is, is there a central repository or something where people can share their Flow nodes? There's no centralized place where people share their Flow nodes. You'll find some online sometimes. I've placed some in the online assets, just because I have access to it. Paul has placed some on GitHub-- the link that he showed earlier. He has a really nice repository of Flow nodes. So I would encourage you to look at them. They're also great examples on what you can do in Flow.

      I also mentioned that, if you go through the entire handout, you'll end up with the Stingray app kit extension. The concepts that are in the handout after what we've done today, if you go through them and you understand them, then you're pretty much good for life with Lua. You go through what classes are, project organization, and co-routines-- things that exist in Unity and in Lua, and how to automate those things. And it goes pretty deep. If you understand that, you're really good. You're good to go. But most people stop here, and they're very happy.

      So I think we're out of time. If there's any-- two minutes. If there's any more questions, I can keep going.

      AUDIENCE: Are there any classes online that you can take?

      DAVID MENARD: Online classes for Lua. Lots of documentation for Lua-- official language documentation-- that you can find online just by googling Lua. The official programming language is not tied to Stingray. Obviously, there's a lot of game engines that use Lua as a scripting language. So whenever you're looking for things like FOR loops or how to declare stuff, Google is your best friend with Lua.

      There are some Lynda.com resources for Lua's classes, that kind of thing. You're going to find them pretty easily. When it comes to Stingray-specific Lua classes, this is pretty much, as far as I know, the only thing that exists. But the concepts here-- it doesn't get much deeper than this. Once you understand this kind of stuff, you're going to get going pretty fast. And then it's just a matter of getting used to it.

      I can stay here all day if you have-- or all night If you have more questions. Yes?

      AUDIENCE: [INAUDIBLE]

      DAVID MENARD: OK?

      AUDIENCE: How do you put breaks in so I can see what values are being fed through?

      DAVID MENARD: OK. The question is that you're having trouble debugging Flow. How do you put breakpoints and see what's happening? Custom Flow nodes-- breakpoints in your custom Flow nodes are the best way to go.

      AUDIENCE: Well, I'm not even doing the custom ones. The stock--

      DAVID MENARD: The standard nodes? I don't know if you have tips. I usually put a lot of debug prints in there. You can add some custom nodes just to feed the inputs and inspect them there.

      ______
      icon-svg-close-thick

      쿠기 기본 설정

      오토데스크는 고객의 개인 정보와 최상의 경험을 중요시합니다. 오토데스크는 정보를 사용자화하고 응용프로그램을 만들기 위해 고객의 본 사이트 사용에 관한 데이터를 수집합니다.

      오토데스크에서 고객의 데이터를 수집하고 사용하도록 허용하시겠습니까?

      오토데스크에서 사용하는타사 서비스개인정보 처리방침 정책을 자세히 알아보십시오.

      반드시 필요 - 사이트가 제대로 작동하고 사용자에게 서비스를 원활하게 제공하기 위해 필수적임

      이 쿠키는 오토데스크에서 사용자 기본 설정 또는 로그인 정보를 저장하거나, 사용자 요청에 응답하거나, 장바구니의 품목을 처리하기 위해 필요합니다.

      사용자 경험 향상 – 사용자와 관련된 항목을 표시할 수 있게 해 줌

      이 쿠키는 오토데스크가 보다 향상된 기능을 제공하고 사용자에게 맞는 정보를 제공할 수 있게 해 줍니다. 사용자에게 맞는 정보 및 환경을 제공하기 위해 오토데스크 또는 서비스를 제공하는 협력업체에서 이 쿠키를 설정할 수 있습니다. 이 쿠키를 허용하지 않을 경우 이러한 서비스 중 일부 또는 전체를 이용하지 못하게 될 수 있습니다.

      광고 수신 설정 – 사용자에게 타겟팅된 광고를 제공할 수 있게 해 줌

      이 쿠키는 사용자와 관련성이 높은 광고를 표시하고 그 효과를 추적하기 위해 사용자 활동 및 관심 사항에 대한 데이터를 수집합니다. 이렇게 데이터를 수집함으로써 사용자의 관심 사항에 더 적합한 광고를 표시할 수 있습니다. 이 쿠키를 허용하지 않을 경우 관심 분야에 해당되지 않는 광고가 표시될 수 있습니다.

      icon-svg-close-thick

      타사 서비스

      각 범주에서 오토데스크가 사용하는 타사 서비스와 온라인에서 고객으로부터 수집하는 데이터를 사용하는 방식에 대해 자세히 알아보십시오.

      icon-svg-hide-thick

      icon-svg-show-thick

      반드시 필요 - 사이트가 제대로 작동하고 사용자에게 서비스를 원활하게 제공하기 위해 필수적임

      Qualtrics
      오토데스크는 고객에게 더욱 시의적절하며 관련 있는 이메일 컨텐츠를 제공하기 위해 Qualtrics를 이용합니다. 이를 위해, 고객의 온라인 행동 및 오토데스크에서 전송하는 이메일과의 상호 작용에 관한 데이터를 수집합니다. 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID, 이메일 확인율, 클릭한 링크 등이 포함될 수 있습니다. 오토데스크는 이 데이터를 다른 소스에서 수집된 데이터와 결합하여 고객의 판매 또는 고객 서비스 경험을 개선하며, 고급 분석 처리에 기초하여 보다 관련 있는 컨텐츠를 제공합니다. Qualtrics 개인정보취급방침
      Akamai mPulse
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 Akamai mPulse를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID 및 오토데스크 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. Akamai mPulse 개인정보취급방침
      Digital River
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 Digital River를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID 및 오토데스크 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. Digital River 개인정보취급방침
      Dynatrace
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 Dynatrace를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID 및 오토데스크 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. Dynatrace 개인정보취급방침
      Khoros
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 Khoros를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID 및 오토데스크 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. Khoros 개인정보취급방침
      Launch Darkly
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 Launch Darkly를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID 및 오토데스크 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. Launch Darkly 개인정보취급방침
      New Relic
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 New Relic를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID 및 오토데스크 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. New Relic 개인정보취급방침
      Salesforce Live Agent
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 Salesforce Live Agent를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID 및 오토데스크 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. Salesforce Live Agent 개인정보취급방침
      Wistia
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 Wistia를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID 및 오토데스크 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. Wistia 개인정보취급방침
      Tealium
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 Tealium를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. Upsellit
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 Upsellit를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. CJ Affiliates
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 CJ Affiliates를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. Commission Factory
      Typepad Stats
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 Typepad Stats를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID 및 오토데스크 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. Typepad Stats 개인정보취급방침
      Geo Targetly
      Autodesk는 Geo Targetly를 사용하여 웹 사이트 방문자를 가장 적합한 웹 페이지로 안내하거나 위치를 기반으로 맞춤형 콘텐츠를 제공합니다. Geo Targetly는 웹 사이트 방문자의 IP 주소를 사용하여 방문자 장치의 대략적인 위치를 파악합니다. 이렇게 하면 방문자가 (대부분의 경우) 현지 언어로 된 콘텐츠를 볼 수 있습니다.Geo Targetly 개인정보취급방침
      SpeedCurve
      Autodesk에서는 SpeedCurve를 사용하여 웹 페이지 로드 시간과 이미지, 스크립트, 텍스트 등의 후속 요소 응답성을 측정하여 웹 사이트 환경의 성능을 모니터링하고 측정합니다. SpeedCurve 개인정보취급방침
      Qualified
      Qualified is the Autodesk Live Chat agent platform. This platform provides services to allow our customers to communicate in real-time with Autodesk support. We may collect unique ID for specific browser sessions during a chat. Qualified Privacy Policy

      icon-svg-hide-thick

      icon-svg-show-thick

      사용자 경험 향상 – 사용자와 관련된 항목을 표시할 수 있게 해 줌

      Google Optimize
      오토데스크는 사이트의 새 기능을 테스트하고 이러한 기능의 고객 경험을 사용자화하기 위해 Google Optimize을 이용합니다. 이를 위해, 고객이 사이트를 방문해 있는 동안 행동 데이터를 수집합니다. 이 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID, 오토데스크 ID 등이 포함될 수 있습니다. 고객은 기능 테스트를 바탕으로 여러 버전의 오토데스크 사이트를 경험하거나 방문자 특성을 바탕으로 개인화된 컨텐츠를 보게 될 수 있습니다. Google Optimize 개인정보취급방침
      ClickTale
      오토데스크는 고객이 사이트에서 겪을 수 있는 어려움을 더 잘 파악하기 위해 ClickTale을 이용합니다. 페이지의 모든 요소를 포함해 고객이 오토데스크 사이트와 상호 작용하는 방식을 이해하기 위해 세션 녹화를 사용합니다. 개인적으로 식별 가능한 정보는 가려지며 수집되지 않습니다. ClickTale 개인정보취급방침
      OneSignal
      오토데스크는 OneSignal가 지원하는 사이트에 디지털 광고를 배포하기 위해 OneSignal를 이용합니다. 광고는 OneSignal 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 OneSignal에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 OneSignal에 제공하는 데이터를 사용합니다. OneSignal 개인정보취급방침
      Optimizely
      오토데스크는 사이트의 새 기능을 테스트하고 이러한 기능의 고객 경험을 사용자화하기 위해 Optimizely을 이용합니다. 이를 위해, 고객이 사이트를 방문해 있는 동안 행동 데이터를 수집합니다. 이 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID, 오토데스크 ID 등이 포함될 수 있습니다. 고객은 기능 테스트를 바탕으로 여러 버전의 오토데스크 사이트를 경험하거나 방문자 특성을 바탕으로 개인화된 컨텐츠를 보게 될 수 있습니다. Optimizely 개인정보취급방침
      Amplitude
      오토데스크는 사이트의 새 기능을 테스트하고 이러한 기능의 고객 경험을 사용자화하기 위해 Amplitude을 이용합니다. 이를 위해, 고객이 사이트를 방문해 있는 동안 행동 데이터를 수집합니다. 이 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID, 오토데스크 ID 등이 포함될 수 있습니다. 고객은 기능 테스트를 바탕으로 여러 버전의 오토데스크 사이트를 경험하거나 방문자 특성을 바탕으로 개인화된 컨텐츠를 보게 될 수 있습니다. Amplitude 개인정보취급방침
      Snowplow
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 Snowplow를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID 및 오토데스크 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. Snowplow 개인정보취급방침
      UserVoice
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 UserVoice를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID 및 오토데스크 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. UserVoice 개인정보취급방침
      Clearbit
      Clearbit를 사용하면 실시간 데이터 보강 기능을 통해 고객에게 개인화되고 관련 있는 환경을 제공할 수 있습니다. Autodesk가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. Clearbit 개인정보취급방침
      YouTube
      YouTube는 사용자가 웹 사이트에 포함된 비디오를 보고 공유할 수 있도록 해주는 비디오 공유 플랫폼입니다. YouTube는 비디오 성능에 대한 시청 지표를 제공합니다. YouTube 개인정보보호 정책

      icon-svg-hide-thick

      icon-svg-show-thick

      광고 수신 설정 – 사용자에게 타겟팅된 광고를 제공할 수 있게 해 줌

      Adobe Analytics
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 Adobe Analytics를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID 및 오토데스크 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. Adobe Analytics 개인정보취급방침
      Google Analytics (Web Analytics)
      오토데스크 사이트에서 고객의 행동에 관한 데이터를 수집하기 위해 Google Analytics (Web Analytics)를 이용합니다. 여기에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 오토데스크는 사이트 성과를 측정하고 고객의 온라인 경험의 편리함을 평가하여 기능을 개선하기 위해 이러한 데이터를 이용합니다. 또한, 이메일, 고객 지원 및 판매와 관련된 고객 경험을 최적화하기 위해 고급 분석 방법도 사용하고 있습니다. AdWords
      Marketo
      오토데스크는 고객에게 더욱 시의적절하며 관련 있는 이메일 컨텐츠를 제공하기 위해 Marketo를 이용합니다. 이를 위해, 고객의 온라인 행동 및 오토데스크에서 전송하는 이메일과의 상호 작용에 관한 데이터를 수집합니다. 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID, 이메일 확인율, 클릭한 링크 등이 포함될 수 있습니다. 오토데스크는 이 데이터를 다른 소스에서 수집된 데이터와 결합하여 고객의 판매 또는 고객 서비스 경험을 개선하며, 고급 분석 처리에 기초하여 보다 관련 있는 컨텐츠를 제공합니다. Marketo 개인정보취급방침
      Doubleclick
      오토데스크는 Doubleclick가 지원하는 사이트에 디지털 광고를 배포하기 위해 Doubleclick를 이용합니다. 광고는 Doubleclick 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 Doubleclick에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 Doubleclick에 제공하는 데이터를 사용합니다. Doubleclick 개인정보취급방침
      HubSpot
      오토데스크는 고객에게 더욱 시의적절하며 관련 있는 이메일 컨텐츠를 제공하기 위해 HubSpot을 이용합니다. 이를 위해, 고객의 온라인 행동 및 오토데스크에서 전송하는 이메일과의 상호 작용에 관한 데이터를 수집합니다. 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID, 이메일 확인율, 클릭한 링크 등이 포함될 수 있습니다. HubSpot 개인정보취급방침
      Twitter
      오토데스크는 Twitter가 지원하는 사이트에 디지털 광고를 배포하기 위해 Twitter를 이용합니다. 광고는 Twitter 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 Twitter에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 Twitter에 제공하는 데이터를 사용합니다. Twitter 개인정보취급방침
      Facebook
      오토데스크는 Facebook가 지원하는 사이트에 디지털 광고를 배포하기 위해 Facebook를 이용합니다. 광고는 Facebook 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 Facebook에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 Facebook에 제공하는 데이터를 사용합니다. Facebook 개인정보취급방침
      LinkedIn
      오토데스크는 LinkedIn가 지원하는 사이트에 디지털 광고를 배포하기 위해 LinkedIn를 이용합니다. 광고는 LinkedIn 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 LinkedIn에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 LinkedIn에 제공하는 데이터를 사용합니다. LinkedIn 개인정보취급방침
      Yahoo! Japan
      오토데스크는 Yahoo! Japan가 지원하는 사이트에 디지털 광고를 배포하기 위해 Yahoo! Japan를 이용합니다. 광고는 Yahoo! Japan 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 Yahoo! Japan에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 Yahoo! Japan에 제공하는 데이터를 사용합니다. Yahoo! Japan 개인정보취급방침
      Naver
      오토데스크는 Naver가 지원하는 사이트에 디지털 광고를 배포하기 위해 Naver를 이용합니다. 광고는 Naver 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 Naver에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 Naver에 제공하는 데이터를 사용합니다. Naver 개인정보취급방침
      Quantcast
      오토데스크는 Quantcast가 지원하는 사이트에 디지털 광고를 배포하기 위해 Quantcast를 이용합니다. 광고는 Quantcast 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 Quantcast에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 Quantcast에 제공하는 데이터를 사용합니다. Quantcast 개인정보취급방침
      Call Tracking
      오토데스크는 캠페인을 위해 사용자화된 전화번호를 제공하기 위하여 Call Tracking을 이용합니다. 그렇게 하면 고객이 오토데스크 담당자에게 더욱 빠르게 액세스할 수 있으며, 오토데스크의 성과를 더욱 정확하게 평가하는 데 도움이 됩니다. 제공된 전화번호를 기준으로 사이트에서 고객 행동에 관한 데이터를 수집할 수도 있습니다. Call Tracking 개인정보취급방침
      Wunderkind
      오토데스크는 Wunderkind가 지원하는 사이트에 디지털 광고를 배포하기 위해 Wunderkind를 이용합니다. 광고는 Wunderkind 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 Wunderkind에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 Wunderkind에 제공하는 데이터를 사용합니다. Wunderkind 개인정보취급방침
      ADC Media
      오토데스크는 ADC Media가 지원하는 사이트에 디지털 광고를 배포하기 위해 ADC Media를 이용합니다. 광고는 ADC Media 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 ADC Media에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 ADC Media에 제공하는 데이터를 사용합니다. ADC Media 개인정보취급방침
      AgrantSEM
      오토데스크는 AgrantSEM가 지원하는 사이트에 디지털 광고를 배포하기 위해 AgrantSEM를 이용합니다. 광고는 AgrantSEM 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 AgrantSEM에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 AgrantSEM에 제공하는 데이터를 사용합니다. AgrantSEM 개인정보취급방침
      Bidtellect
      오토데스크는 Bidtellect가 지원하는 사이트에 디지털 광고를 배포하기 위해 Bidtellect를 이용합니다. 광고는 Bidtellect 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 Bidtellect에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 Bidtellect에 제공하는 데이터를 사용합니다. Bidtellect 개인정보취급방침
      Bing
      오토데스크는 Bing가 지원하는 사이트에 디지털 광고를 배포하기 위해 Bing를 이용합니다. 광고는 Bing 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 Bing에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 Bing에 제공하는 데이터를 사용합니다. Bing 개인정보취급방침
      G2Crowd
      오토데스크는 G2Crowd가 지원하는 사이트에 디지털 광고를 배포하기 위해 G2Crowd를 이용합니다. 광고는 G2Crowd 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 G2Crowd에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 G2Crowd에 제공하는 데이터를 사용합니다. G2Crowd 개인정보취급방침
      NMPI Display
      오토데스크는 NMPI Display가 지원하는 사이트에 디지털 광고를 배포하기 위해 NMPI Display를 이용합니다. 광고는 NMPI Display 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 NMPI Display에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 NMPI Display에 제공하는 데이터를 사용합니다. NMPI Display 개인정보취급방침
      VK
      오토데스크는 VK가 지원하는 사이트에 디지털 광고를 배포하기 위해 VK를 이용합니다. 광고는 VK 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 VK에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 VK에 제공하는 데이터를 사용합니다. VK 개인정보취급방침
      Adobe Target
      오토데스크는 사이트의 새 기능을 테스트하고 이러한 기능의 고객 경험을 사용자화하기 위해 Adobe Target을 이용합니다. 이를 위해, 고객이 사이트를 방문해 있는 동안 행동 데이터를 수집합니다. 이 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역, IP 주소 또는 장치 ID, 오토데스크 ID 등이 포함될 수 있습니다. 고객은 기능 테스트를 바탕으로 여러 버전의 오토데스크 사이트를 경험하거나 방문자 특성을 바탕으로 개인화된 컨텐츠를 보게 될 수 있습니다. Adobe Target 개인정보취급방침
      Google Analytics (Advertising)
      오토데스크는 Google Analytics (Advertising)가 지원하는 사이트에 디지털 광고를 배포하기 위해 Google Analytics (Advertising)를 이용합니다. 광고는 Google Analytics (Advertising) 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 Google Analytics (Advertising)에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 Google Analytics (Advertising)에 제공하는 데이터를 사용합니다. Google Analytics (Advertising) 개인정보취급방침
      Trendkite
      오토데스크는 Trendkite가 지원하는 사이트에 디지털 광고를 배포하기 위해 Trendkite를 이용합니다. 광고는 Trendkite 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 Trendkite에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 Trendkite에 제공하는 데이터를 사용합니다. Trendkite 개인정보취급방침
      Hotjar
      오토데스크는 Hotjar가 지원하는 사이트에 디지털 광고를 배포하기 위해 Hotjar를 이용합니다. 광고는 Hotjar 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 Hotjar에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 Hotjar에 제공하는 데이터를 사용합니다. Hotjar 개인정보취급방침
      6 Sense
      오토데스크는 6 Sense가 지원하는 사이트에 디지털 광고를 배포하기 위해 6 Sense를 이용합니다. 광고는 6 Sense 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 6 Sense에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 6 Sense에 제공하는 데이터를 사용합니다. 6 Sense 개인정보취급방침
      Terminus
      오토데스크는 Terminus가 지원하는 사이트에 디지털 광고를 배포하기 위해 Terminus를 이용합니다. 광고는 Terminus 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 Terminus에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 Terminus에 제공하는 데이터를 사용합니다. Terminus 개인정보취급방침
      StackAdapt
      오토데스크는 StackAdapt가 지원하는 사이트에 디지털 광고를 배포하기 위해 StackAdapt를 이용합니다. 광고는 StackAdapt 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 StackAdapt에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 StackAdapt에 제공하는 데이터를 사용합니다. StackAdapt 개인정보취급방침
      The Trade Desk
      오토데스크는 The Trade Desk가 지원하는 사이트에 디지털 광고를 배포하기 위해 The Trade Desk를 이용합니다. 광고는 The Trade Desk 데이터와 고객이 사이트를 방문하는 동안 오토데스크가 수집하는 행동 데이터 모두에 기초하여 제공됩니다. 오토데스크가 수집하는 데이터에는 고객이 방문한 페이지, 시작한 체험판, 재생한 동영상, 구매 내역 및 IP 주소 또는 장치 ID가 포함될 수 있습니다. 이 정보는 The Trade Desk에서 고객으로부터 수집한 데이터와 결합될 수 있습니다. 오토데스크는 디지털 광고 경험에 대한 사용자화를 개선하고 고객에게 더욱 관련 있는 광고를 제시하기 위해 The Trade Desk에 제공하는 데이터를 사용합니다. The Trade Desk 개인정보취급방침
      RollWorks
      We use RollWorks to deploy digital advertising on sites supported by RollWorks. Ads are based on both RollWorks data and behavioral data that we collect while you’re on our sites. The data we collect may include pages you’ve visited, trials you’ve initiated, videos you’ve played, purchases you’ve made, and your IP address or device ID. This information may be combined with data that RollWorks has collected from you. We use the data that we provide to RollWorks to better customize your digital advertising experience and present you with more relevant ads. RollWorks Privacy Policy

      정말 더 적은 온라인 경험을 원하십니까?

      오토데스크는 고객 여러분에게 좋은 경험을 드리고 싶습니다. 이전 화면의 범주에 대해 "예"를 선택하셨다면 오토데스크는 고객을 위해 고객 경험을 사용자화하고 향상된 응용프로그램을 제작하기 위해 귀하의 데이터를 수집하고 사용합니다. 언제든지 개인정보 처리방침을 방문해 설정을 변경할 수 있습니다.

      고객의 경험. 고객의 선택.

      오토데스크는 고객의 개인 정보 보호를 중요시합니다. 오토데스크에서 수집하는 정보는 오토데스크 제품 사용 방법, 고객이 관심을 가질 만한 정보, 오토데스크에서 더욱 뜻깊은 경험을 제공하기 위한 개선 사항을 이해하는 데 도움이 됩니다.

      오토데스크에서 고객님께 적합한 경험을 제공해 드리기 위해 고객님의 데이터를 수집하고 사용하도록 허용하시겠습니까?

      선택할 수 있는 옵션을 자세히 알아보려면 이 사이트의 개인 정보 설정을 관리해 사용자화된 경험으로 어떤 이점을 얻을 수 있는지 살펴보거나 오토데스크 개인정보 처리방침 정책을 확인해 보십시오.