Polygonal Mind
  • Home
  • Metaverse Builder
  • CryptoAvatars
  • MegaCube
  • Blog
  • Decentraland
  • Projects
  • Assets

Blog

Guía para modificar el Plugin de Decentraland para Unity

5/14/2020

0 Comments

 
English
Por qué necesito modificar el Plugin de Unity?
Usar el SDK de Decentraland para poner entidades "manualmente" en el código de typescript puede ser un proceso lento y tedioso, especialmente si tienes que colocar muchos de ellos y tienes que comprobar que estén todos en el sitio correcto.

Por eso necesitas herramientas que ayuden a construir una escena, como el Decentraland builder o el Unity to DCL export plugin. Aun así, hay ciertos límites sobre lo que puedes hacer con ellos.

En esta guía vamos a ver cómo hacer modificaciones al Unity plugin para DCL para expandir el número de cosas que puedes exportar al código de Decentraland desde el editor de Unity.
​
Antes de empezar con la guía deberías tener un mínimo de conocimientos del SDK de Decentraland, cómo exportar una escena desde Unity para DCL y algo de programación (usaremos typescript y C#).
Decentraland Documentation
https://docs.decentraland.org/development-guide/coding-scenes/
https://docs.decentraland.org/blockchain-integration/display-a-certified-nft/
El Objetivo

Vamos a enseñar un ejemplo de cómo modificar el plugin para hacerle exportar a typescript información necesaria para colocar NFTs en una escena.
Tomando esto como base, deberías ser capaz de hacer tus propias modificaciones que se adapten a las necesidades de tu proyecto.
Recursos

  • Unity 2018.3.6f1
  • Visual Studio (recomendado para usar junto a Unity)
  • Decentraland SDK
  • Unity to DCL export plugin
  • Decentraland para principiantes
Modificaciones en el plugin de Unity de Decentraland
Preparar el proyecto de Typescript

Antes de nada, vamos a programar nuestro propio componente y a dejarlo listo para usar en el proyecto de Typescript.
https://docs.decentraland.org/development-guide/entities-components/#custom-components
No es necesario hacer un componente propio para poner un NFTShape (que ya es un componente de por sí), pero para esta guía lo haremos igualmente, además será últil para usarlo como base para tus propios componentes más complejos.
Antes de empezar a escribir el código en tu archivo game.ts, ten en cuenta que el plugin de Unity sobreescribe el archivo entero y todo tu código se perderá. Por ello debemos trabajar en un archivo separado.
​
Crea un nuevo archivo en src/imports/components/NFT.ts con el siguiente código:
//Crea un componente NFTShape con la información dada
export function createNFTComponent(entity: IEntity, smartContract: string, tokenId: string){
entity.addComponent(
new NFTShape('ethereum://'+smartContract+'/'+tokenId,Color3.Blue())
)
}
//Añade un componente NFTdata a la entidad, crea un componente NFTcon la información dada
@Component('NFTdata')
export class NFTdata{
entity: IEntity //entidad del NFT
smartContract: string //Smart contract del NFT
tokenId: string //Token ID del NFT
constructor(entity: IEntity, smartContract: string, tokenId: string){
this.entity = entity
this.smartContract = smartContract
this.tokenId = tokenId
createNFTComponent(entity, smartContract, tokenId)
}
}
Crea otro script en src/imports/index.ts con:
export { NFTdata, createNFTComponent } from "./components/NFT"
Ahora tenemos nuestro código listo para cuando el plugin exporte el game.ts
Haz un script de Unity que contenga la información a exportar

Crea un script vacío en Unity y ábrelo con Visual Studio (o tu editor de código favorito).
Unity crear un nuevo script de C#
En este ejemplo, solo necesitamos guardar la información del NFT, pero siéntente libre de añadir lo que necesitas para tu propio proyecto.
public class nft_script : MonoBehaviour
{
public string smartContract;
public string tokenId;
}
Añade este script a la entidad de la escena que quieras que tenga el componente del NFT y rellena la información.
​
Address: 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d
Token id:
​558536
Información del NFT en el inspector de Unity
Ahora tenemos la información guardada en nuestra entidad, el último paso que falta es traducir estos datos a nuestro proyecto en typsecript.
Modificando el plugin de Unity en C#

Modificar el plugin puede parecer una tarea muy complicada, pero ten en cuenta que solo necesitamos añadir nuestras pequeñas piezas de código para expandir sus funcionalidades; muy sencillo de hacer si sabes dónde hacerlo.
​
Abre SceneTraverser.cs en la carpeta del plugin de Decentraland: Assets/Decentraland/SceneTraverser.cs

Encuentra la función public static ResourceRecorder TraverseAllScene y añade el siguiente código después del comentario //====== Start Traversing ======
//====== Start Traversing ======
if (exportStr != null)
{
exportStr.AppendLine("import { NFTdata } from \"./imports/index\"\n\n");
}
Esto importará nuestra clase NFTdata al inicio de game.ts
​
Después encuentra la función public static void RecursivelyTraverseTransform y después de exportStr.AppendFormat(NewEntityWithName, entityName, tra.name); añade:
nft_script nftObject = (tra.gameObject.GetComponent("nft_script") as nft_script);
if (nftObject)
{
exportStr.AppendFormat(SetNFT, entityName, nftObject.smartContract, nftObject.tokenId);
}
Último paso,
​encuentra dónde están declaradas las strings de exportación y añade la string del SetNFT al final.
SceneTraverser code
private const string SetNFT = "{0}.addComponent(new NFTdata({0}, \"{1}\", \"{2}\")) \n";
Este código comprobará si la entidad exportada tiene un nft_script y le añadirá el componente NFT data a la entidad dentro de nuestro archivo game.ts.
​
Con todo esto terminado, tu escena está lista para ser exportada a un proyecto de typescript y tu game.ts resultante debería parecerse a esto:
import { NFTdata } from "./imports/index"

var entity1372n = new Entity("NFTentity")
entity1372n.addComponent(new NFTdata(entity1372n, "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d", "558536"))
engine.addEntity(entity1372n)
entity1372n.addComponent(new Transform({ position: new Vector3(6, 1.5, 6) }))
entity1372n.getComponent(Transform).rotation.set(0, 0, 0, 1)
entity1372n.getComponent(Transform).scale.set(1, 1, 1)
Consejos finales

Puedes acceder desde cualquier parte en el proyecto a cualquier array de entidades con tus componentes personalizados, y esto puede ser útil para controlar dónde y cómo algunos componentes o comportamientos empiezan.
engine.getEntitiesWithComponent(NFTdata)
No necesitas hacer nuevos componentes muy complejos si no lo necesitas, por ejemplo puedes exportar desde Unity solamente la información para añadirle a una entidad en un array y aplicarle un comportamiento personalizado.

Antes de empezar a hacer tus propios compontentes, echa un vistazo al sdk de Decentraland y a la librería de utilidades de Decentraland, puede que ya tengan hecho lo que estás buscando.
​
Referencia a la API de ECS:
https://github.com/decentraland/ecs-reference
decentraland-ecs-utils:
https://github.com/decentraland/decentraland-ecs-utils
npm install decentraland-ecs-utils
Conclusión

Si has entendido los pasos de ésta guía, estás list@ para hacer tus propios componentes para Decentraland y poder utilizarlos en el Editor de Unity, y será de gran ayuda para llenar tus escenas con gameplay e interacciones. Espero que te haga el proceso de desarrollo para Decentraland un poquito más fácil.

Picture
Alex Picazo
PROGRAMADOR
Videogame programmer, love developing cool stuff. Always searching for new interesting stories.
LinkedIn
0 Comments

Guide to make modifications in the Decentraland Unity plugin

5/14/2020

0 Comments

 
Español
Why do I need to modify the Unity plugin?
Using the Decentraland SDK to place entities "manually" in the typescript code may be a slow and tedious process, especially if you have to put a lot of them and you have to look one by one if they are in the right place.

That's why you have tools to help you build a scene, like the Decentraland builder or the Unity to DCL export plugin. However there are a limited set of things you can do with them.

In this guide we'll see how to make custom modifications to the Unity plugin for DCL to expand the number of things you can export to code from the Unity scene editor.
​
Before starting with this guide you should have a minimun knowledge of the Decentraland SDK, the basics of how to export a scene from unity to DCL and some programing basics (we'll use typescript and C#).
Decentraland Documentation
https://docs.decentraland.org/development-guide/coding-scenes/
https://docs.decentraland.org/blockchain-integration/display-a-certified-nft/
The Mission

We'll do an example modification of the plugin to make it export to typescript the necessary data to place an NFT in the scene.
Taking this example as base, you should be able to make your own custom modifications to meet your project needs.
Resources

  • Unity version 2018.3.6f1
  • Visual Studio (recomended to use with unity)
  • Decentraland SDK
  • Unity to DCL export plugin
  • Decentraland for starters guide
Modifications in the decentraland unity plugin
Prepare your Typescript project

First of all, we are going to code our custom component and have it ready to use in the typescript project.
https://docs.decentraland.org/development-guide/entities-components/#custom-components
It isn't necessary to make a custom component to place an NFTShape (which is already a component), but for the sake of the guide we'll do it anyway, besides it will be useful for you as a base to make your own more complex components.
Before start writing code in your game.ts file, be aware that the Unity export plugin will ovewrite the entire game.ts file and your code will be lost, because of this we need to work in a separate file.
​
Create a new file src/imports/components/NFT.ts with the following code:
//Creates an NFTShape component with the given info
export function createNFTComponent(entity: IEntity, smartContract: string, tokenId: string){
entity.addComponent(
new NFTShape('ethereum://'+smartContract+'/'+tokenId,Color3.Blue())
)
}
//Add a NFTdata component to the entity, creates an NFT component with the given info
@Component('NFTdata')
export class NFTdata{
entity: IEntity //entity of the NFT
smartContract: string //Smart contract of the NFT
tokenId: string //Token ID of the NFT
constructor(entity: IEntity, smartContract: string, tokenId: string){
this.entity = entity
this.smartContract = smartContract
this.tokenId = tokenId
createNFTComponent(entity, smartContract, tokenId)
}
}
And another one src/imports/index.ts with:
export { NFTdata, createNFTComponent } from "./components/NFT"
Now we have our code ready for when our modified plugin exports the game.ts
Make a Unity script to hold the data to export

Create a empty script in Unity and open it in visual studio (or your favorite code editor).
Empty C# script in Unity
You only need to store here the NFT address info for this guide, but fell free to add anything you need for your project.
public class nft_script : MonoBehaviour
{
public string smartContract;
public string tokenId;
}
Add this script to the entity in the scene you want to have the NFT as component and fill the address data.
​
Address: 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d
Token id:
​558536
NFT data in the Unity inspector
Now we have the data stored in our entity, the only step left is to modify the plugin to translate this data to our project in typescript.
Modify the Unity plugin in C#

Modifing the plugin may seem like an overwhelming task, but keep in mind we only need to add our little pieces of code in it and expand its functionalities, simple if you know where to do it.
​
Open SceneTraverser.cs in the Decentraland plugin folder: Assets/Decentraland/SceneTraverser.cs
​
Find the public static ResourceRecorder TraverseAllScene function and add the following code after the comment //====== Start Traversing ======
//====== Start Traversing ======
if (exportStr != null)
{
exportStr.AppendLine("import { NFTdata } from \"./imports/index\"\n\n");
}
This will import our NFTdata class at the start of game.ts
​
Next find the public static void RecursivelyTraverseTransform function and after exportStr.AppendFormat(NewEntityWithName, entityName, tra.name); add this code:
nft_script nftObject = (tra.gameObject.GetComponent("nft_script") as nft_script);
if (nftObject)
{
exportStr.AppendFormat(SetNFT, entityName, nftObject.smartContract, nftObject.tokenId);
}
Last step, find the place where the export strings are declared and add the SetNFT string at the end.
SceneTraverser code
private const string SetNFT = "{0}.addComponent(new NFTdata({0}, \"{1}\", \"{2}\")) \n";
This code will check if the exported entity has an nft_script and will add the NFT data component to the entity in our game.ts file.
With all done your scene is ready to be exported to a typescript project and your resulting game.ts should look like this:
import { NFTdata } from "./imports/index"

var entity1372n = new Entity("NFTentity")
entity1372n.addComponent(new NFTdata(entity1372n, "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d", "558536"))
engine.addEntity(entity1372n)
entity1372n.addComponent(new Transform({ position: new Vector3(6, 1.5, 6) }))
entity1372n.getComponent(Transform).rotation.set(0, 0, 0, 1)
entity1372n.getComponent(Transform).scale.set(1, 1, 1)
Final tips

You can access from anywhere in the project to an array of entities with your custom components, this can be usefull to control where and how some components or behaviours start.
engine.getEntitiesWithComponent(NFTdata)
You don't need to make complex new components if you don't have to, for example you can export from unity only the info to put an entity in an array and apply to them a custom behaviour.
Before start making your componets, take a look at the decentraland sdk and the decentraland utils library, they may have what you are looking for.
ECS API Reference:
https://github.com/decentraland/ecs-reference
decentraland-ecs-utils:
https://github.com/decentraland/decentraland-ecs-utils
npm install decentraland-ecs-utils
Conclusion

If you have understood the steps done in this guide, you are ready now to make your own components for decentraland and place them using the Unity editor, this will be very usefull to fill your scenes with gameplay and interaction. I hope it makes your development process for decentraland easier.

Picture
Alex Picazo
PROGRAMMER
Videogame programmer, love developing cool stuff. Always searching for new interesting stories.
LinkedIn
0 Comments

Transacciones de MANA en Decentraland

5/11/2020

0 Comments

 
English
Objetivo

Esta es una guía corta enfocada a aprender a programar con Typescript una transacción de MANA en Decentraland.
Recursos

  • Decentraland SDK
  • Basic DCL project
  • Metamask extension (solo para test)
Transacciones de MANA en Decentraland
Documentación oficial:

https://docs.decentraland.org/blockchain-integration/scene-blockchain-operations/
Librerías e imports al proyecto:

Para esto necesitarás instalar en tu proyecto de Typescript la libreria eth-connect para hacer de interfaz con los contratos de Ethereum y poder llamar a sus funciones.
npm install eth-connect
Además, necesitarás importar las siguientes funciones al archivo:

getUserAccount para obtener la dirección de Ethereum del usuario.

getProvider para crear una instancia del proveedor web3 a la interfaz de Metamask.
​
Ambos, getUserAccount y getProvider, son proporcionados por el SDK de Decentraland.
import { getProvider } from '@decentraland/web3-provider'
import { getUserAccount } from '@decentraland/EthereumController'
import * as EthConnect from '../node_modules/eth-connect/esm'
Para que estas librerías funcionen en la previsualización de DCL en modo localhost, necesitarás pegar &ENABLE_WEB3 a la URL.
​
Ejemplo:
​http://192.168.0.112:8000/?SCENE_DEBUG_PANEL&position=12%2C44&ENABLE_WEB3
Antes de empezar:

Para poder realizar un pago mediante MANA necesitas varias cosas...
La dirección de Ethereum a pagar el MANA, esta es la dirección que recibirá el MANA de los pagos de los usuarios:
 Ejemplo: const PAYMENT_ADDRESS ="0x4tU......8dY"
La cantidad de MANA que el usuario pagará.
 Ejemplo: const MANA_PAYMENT = 10
La dirección del contrato de MANA:
const MANA_ADDRESS = "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942"
La dirección del contrato de MANA falso (opcional), solo si quieres primero probar transacciones con dinero falso (MANA):
const FAKE_MANA_ADDRESS = "0x2a8fd99c19271f4f04b1b7b9c4f7cf264b626edb"
MANA contract ABI:
https://etherscan.io/address/0x0f5d2fb29fb7d3cfee444a200298f468908cc942#code
Crea un archivo vacío en tu proyecto, "/contracts/mana.ts" y pega el MANA ABI en export const abi, o pega directamente el contenido del siguiente archivo.
  • Ocultar
  • Mostrar
<
>
export const abi = [{"constant":true,"inputs":[],"name":"mintingFinished","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"uint256"}],"name":"burn","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"finishMinting","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[],"name":"MintFinished","type":"event"},{"anonymous":false,"inputs":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"burner","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}
Contrato de MANA ABI falso (opcional)
  • Ocultar
  • Mostrar
<
>
​export const abi = [
{
constant: true,
inputs: [],
name: 'mintingFinished',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [],
name: 'name',
outputs: [
{
name: '',
type: 'string'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [
{
name: '_spender',
type: 'address'
},
{
name: '_value',
type: 'uint256'
}
],
name: 'approve',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [],
name: 'totalSupply',
outputs: [
{
name: '',
type: 'uint256'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [
{
name: '_from',
type: 'address'
},
{
name: '_to',
type: 'address'
},
{
name: '_value',
type: 'uint256'
}
],
name: 'transferFrom',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [],
name: 'decimals',
outputs: [
{
name: '',
type: 'uint8'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [],
name: 'unpause',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [
{
name: '_to',
type: 'address'
},
{
name: '_amount',
type: 'uint256'
}
],
name: 'mint',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [
{
name: '_value',
type: 'uint256'
}
],
name: 'burn',
outputs: [],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [],
name: 'paused',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [
{
name: '_owner',
type: 'address'
}
],
name: 'balanceOf',
outputs: [
{
name: 'balance',
type: 'uint256'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [],
name: 'finishMinting',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [],
name: 'pause',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [],
name: 'owner',
outputs: [
{
name: '',
type: 'address'
}
],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [],
name: 'symbol',
outputs: [
{
name: '',
type: 'string'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [
{
name: '_to',
type: 'address'
},
{
name: '_value',
type: 'uint256'
}
],
name: 'transfer',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [
{
name: '_owner',
type: 'address'
},
{
name: '_spender',
type: 'address'
}
],
name: 'allowance',
outputs: [
{
name: 'remaining',
type: 'uint256'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [
{
name: 'to',
type: 'address'
},
{
name: 'amount',
type: 'uint256'
}
],
name: 'setBalance',
outputs: [],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [
{
name: 'newOwner',
type: 'address'
}
],
name: 'transferOwnership',
outputs: [],
payable: false,
type: 'function'
},
{
anonymous: false,
inputs: [
{
indexed: true,
name: 'to',
type: 'address'
},
{
indexed: false,
name: 'amount',
type: 'uint256'
}
],
name: 'Mint',
type: 'event'
},
{
anonymous: false,
inputs: [],
name: 'MintFinished',
type: 'event'
},
{
anonymous: false,
inputs: [],
name: 'Pause',
type: 'event'
},
{
anonymous: false,
inputs: [],
name: 'Unpause',
type: 'event'
},
{
anonymous: false,
inputs: [
{
indexed: true,
name: 'burner',
type: 'address'
},
{
indexed: false,
name: 'value',
type: 'uint256'
}
],
name: 'Burn',
type: 'event'
},
{
anonymous: false,
inputs: [
{
indexed: true,
name: 'owner',
type: 'address'
},
{
indexed: true,
name: 'spender',
type: 'address'
},
{
indexed: false,
name: 'value',
type: 'uint256'
}
],
name: 'Approval',
type: 'event'
},
{
anonymous: false,
inputs: [
{
indexed: true,
name: 'from',
type: 'address'
},
{
indexed: true,
name: 'to',
type: 'address'
},
{
indexed: false,
name: 'value',
type: 'uint256'
}
],
name: 'Transfer',
type: 'event'
}
]
Código

Ahora que ya tenemos recopilada toda la información y las librerías instaladas es hora de desarrollar el código.
Importa las funciones que necesites al principio del archivo.
import { getProvider } from '@decentraland/web3-provider'
import { getUserAccount } from '@decentraland/EthereumController'
import * as EthConnect from '../node_modules/eth-connect/esm'
Importa el MANA ABI (o el falso MANA ABI para hacer pruebas)
import { abi } from './contracts/mana'
Ahora ya puedes crear una función de pago y llamarla a uso cuando quieras hacer que el usuario realice la transacción. 
const PAYMENT_ADDRESS ="0x4tU......8dY"
const MANA_ADDRESS = "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942"
const MANA_PAYMENT = 10
function payment(callback = function(error?, result?){}) {
  executeTask(async () => {
    try {
      const provider = await getProvider()
      const requestManager = new EthConnect.RequestManager(provider)

      const factory = new EthConnect.ContractFactory(requestManager, abi)
      const contract = (await factory.at(
        MANA_ADDRESS
      )) as any
      const address = await getUserAccount()

      const res = await contract.transfer(
         PAYMENT_ADDRESS, MANA_PAYMENT*1000000000000000000,
         { from: address }
      )
    } catch (error) {
      callback(error)
      log(error.toString())
    }
  })
Cuando esta función es llamada, Metamask abrirá una ventana emergente pidiendo la confirmación del pago, si ocurriere algun problema con el pago, durante el pago, o el usuario rechazara este pago, la función payment() capturara un error. Si lo deseas puedes poner tu propia función para gestionar el error.
payment(function(error, result){
if (!error) {
//Payment success
}
else{
//Payment fail
}
})
Pruebas con pagos mediante MANA falso

Antes de que hagas funcionar tu código por primera vez tal vez quieras probarlo primero con MANA falso y Ethereum falso. Es una buena idea para comprobar que todo el código y su funcionalidad están funcionando correctamente y no perder MANA real en el proceso de creación.
Primero de todo necesitas cambiar tu tipo de red en Metamask al modo Ropsten Test Network.
MetaMask Ropsten Test Network
Segundo, necesitas obtener gratuitamente Ether y MANA falso en la red Ropsten.
Falso Ethereum: 
https://faucet.ropsten.be/
Falso MANA: 
https://faucet.decentraland.today/
MetaMask con Ethereum y MANA falso
Por último, necesitas cambiar la dirección de contrato de MANA colocada en el código al igual que cambiar el MANA ABI por los falsos.
import { abi } from './contracts/fakemana'
const MANA_ADDRESS = "0x2a8fd99c19271f4f04b1b7b9c4f7cf264b626edb"
Con estos cambios las pruebas en los pagos de MANA se harán bajo la Ropsten Test Network, y se enviarán a la PAYMENT_ADDRESS de la misma red también.

Picture
Alex Picaxo
PROGRAMADOR
Videogame programmer, love developing cool stuff. Always searching for new interesting stories.
LinkedIn
0 Comments

Simple MANA transactions in DCL

5/8/2020

1 Comment

 
Español
The Mission

This is a short guide on how to code in typescript, a simple MANA payment for your scene in Decentraland.
Resources

  • Decentraland SDK
  • Basic DCL project
  • Metamask extension (for test only)
How to make simple MANA transactions in Decentraland
Decentraland official documentation

https://docs.decentraland.org/blockchain-integration/scene-blockchain-operations/
Libraries and imports

You need to install in your typescript project the eth-connect library, to interface with Ethereum contracts and call their functions.
npm install eth-connect
Also, you will need to import the following functions in your file.
​
getUserAccount to obtain the user's ethereum address.

getProvider to create an instance of the web3 provider to interface with Metamask.
​
getUserAccount and getProvider are provided by the decentraland SDK.
import { getProvider } from '@decentraland/web3-provider'
import { getUserAccount } from '@decentraland/EthereumController'
import * as EthConnect from '../node_modules/eth-connect/esm'​
For this libraries to work in the DCL localhost preview, you'll need to paste &ENABLE_WEB3 to the url, example: http://192.168.0.112:8000/?SCENE_DEBUG_PANEL&position=12%2C44&ENABLE_WEB3
Important information needed before start

To perform a MANA payment you need to gather some info.
Ethereum address to pay the mana, this will be the address that will recive the MANA from the user's payments.
example: const PAYMENT_ADDRESS ="0x4tU......8dY"
MANA units you want the user to pay
example: const MANA_PAYMENT = 10
The MANA ethereum contract address
const MANA_ADDRESS = "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942"
Fake MANA contract address (optional), only if you want to do test transations with Fake MANA
const FAKE_MANA_ADDRESS = "0x2a8fd99c19271f4f04b1b7b9c4f7cf264b626edb"
The MANA contract ABI
https://etherscan.io/address/0x0f5d2fb29fb7d3cfee444a200298f468908cc942#code
Create a empty file inside your project, "/contracts/mana.ts" and paste the MANA ABI in export const abi, or paste the contents of this file.
  • Hide
  • Show
<
>
export const abi = [{"constant":true,"inputs":[],"name":"mintingFinished","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"uint256"}],"name":"burn","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"finishMinting","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[],"name":"MintFinished","type":"event"},{"anonymous":false,"inputs":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"burner","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}
Fake MANA contract ABI (optional)
  • Hide
  • Show
<
>
export const abi = [
{
constant: true,
inputs: [],
name: 'mintingFinished',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [],
name: 'name',
outputs: [
{
name: '',
type: 'string'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [
{
name: '_spender',
type: 'address'
},
{
name: '_value',
type: 'uint256'
}
],
name: 'approve',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [],
name: 'totalSupply',
outputs: [
{
name: '',
type: 'uint256'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [
{
name: '_from',
type: 'address'
},
{
name: '_to',
type: 'address'
},
{
name: '_value',
type: 'uint256'
}
],
name: 'transferFrom',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [],
name: 'decimals',
outputs: [
{
name: '',
type: 'uint8'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [],
name: 'unpause',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [
{
name: '_to',
type: 'address'
},
{
name: '_amount',
type: 'uint256'
}
],
name: 'mint',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [
{
name: '_value',
type: 'uint256'
}
],
name: 'burn',
outputs: [],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [],
name: 'paused',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [
{
name: '_owner',
type: 'address'
}
],
name: 'balanceOf',
outputs: [
{
name: 'balance',
type: 'uint256'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [],
name: 'finishMinting',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [],
name: 'pause',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [],
name: 'owner',
outputs: [
{
name: '',
type: 'address'
}
],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [],
name: 'symbol',
outputs: [
{
name: '',
type: 'string'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [
{
name: '_to',
type: 'address'
},
{
name: '_value',
type: 'uint256'
}
],
name: 'transfer',
outputs: [
{
name: '',
type: 'bool'
}
],
payable: false,
type: 'function'
},
{
constant: true,
inputs: [
{
name: '_owner',
type: 'address'
},
{
name: '_spender',
type: 'address'
}
],
name: 'allowance',
outputs: [
{
name: 'remaining',
type: 'uint256'
}
],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [
{
name: 'to',
type: 'address'
},
{
name: 'amount',
type: 'uint256'
}
],
name: 'setBalance',
outputs: [],
payable: false,
type: 'function'
},
{
constant: false,
inputs: [
{
name: 'newOwner',
type: 'address'
}
],
name: 'transferOwnership',
outputs: [],
payable: false,
type: 'function'
},
{
anonymous: false,
inputs: [
{
indexed: true,
name: 'to',
type: 'address'
},
{
indexed: false,
name: 'amount',
type: 'uint256'
}
],
name: 'Mint',
type: 'event'
},
{
anonymous: false,
inputs: [],
name: 'MintFinished',
type: 'event'
},
{
anonymous: false,
inputs: [],
name: 'Pause',
type: 'event'
},
{
anonymous: false,
inputs: [],
name: 'Unpause',
type: 'event'
},
{
anonymous: false,
inputs: [
{
indexed: true,
name: 'burner',
type: 'address'
},
{
indexed: false,
name: 'value',
type: 'uint256'
}
],
name: 'Burn',
type: 'event'
},
{
anonymous: false,
inputs: [
{
indexed: true,
name: 'owner',
type: 'address'
},
{
indexed: true,
name: 'spender',
type: 'address'
},
{
indexed: false,
name: 'value',
type: 'uint256'
}
],
name: 'Approval',
type: 'event'
},
{
anonymous: false,
inputs: [
{
indexed: true,
name: 'from',
type: 'address'
},
{
indexed: true,
name: 'to',
type: 'address'
},
{
indexed: false,
name: 'value',
type: 'uint256'
}
],
name: 'Transfer',
type: 'event'
}
]
Code

Now that we have all the info and libraries we need it's time to develop or code.
Import the functions you'll need at the begining of the file.
import { getProvider } from '@decentraland/web3-provider'
import { getUserAccount } from '@decentraland/EthereumController'
import * as EthConnect from '../node_modules/eth-connect/esm'
Import the MANA ABI (or the fake MANA ABI for test only)
import { abi } from './contracts/mana'
Now you can create a simple payment function, to call when you want the user to pay.
const PAYMENT_ADDRESS ="0x4tU......8dY"
const MANA_ADDRESS = "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942"
const MANA_PAYMENT = 10
function payment(callback = function(error?, result?){}) {
  executeTask(async () => {
    try {
      const provider = await getProvider()
      const requestManager = new EthConnect.RequestManager(provider)

      const factory = new EthConnect.ContractFactory(requestManager, abi)
      const contract = (await factory.at(
        MANA_ADDRESS
      )) as any
      const address = await getUserAccount()

      const res = await contract.transfer(
         PAYMENT_ADDRESS, MANA_PAYMENT*1000000000000000000,
         { from: address }
      )
    } catch (error) {
      callback(error)
      log(error.toString())
    }
  })
When this function is called, the user's Metamask extension will popup a comfirmation for the payment, if there is a problem with the payment or the user rejects it, the payment() function will throw an error. If oyu want you can add your own callback(error, result) function to manage the success or fail of the payment request.
payment(function(error, result){
if (!error) {
//Payment success
}
else{
//Payment fail
}
})
Test with fake MANA payments

Before you run your code for the first time, maybe you want to test it with fake MANA and fake etherium, to check if everything works fine, and don't lose real MANA in the proccess.
First, you need to change the network in your Metamask extension to the Ropsten Test Network.
MetaMask Ropsten Test Network
Second, you need to obtain free fake ethereum and mana in the Ropsten Test Network.
Fake ethereum: 
https://faucet.ropsten.be/
Fake MANA: 
https://faucet.decentraland.today/
MetaMask with fake Ethereum and MANA
For the last step, you need to change the MANA contract address and the MANA ABI in your code for the fake ones.
import { abi } from './contracts/fakemana'
const MANA_ADDRESS = "0x2a8fd99c19271f4f04b1b7b9c4f7cf264b626edb"
With this your test MANA payments will be done with the fake in the Ropsten Test Network, and will be sent to the PAYMENT_ADDRESS in the Ropsten network as well.

Picture
Alex Picazo
PROGRAMMER
Videogame programmer, love developing cool stuff. Always searching for new interesting stories.
LinkedIn
1 Comment

Desarrollando la estética de SaTTi

5/4/2020

0 Comments

 
English
Picture
Con el lanzamiento al público de Decentraland, se me dio el block out de la escena, en la cual estaba encargado de reorganizar y texturizar basandome en el moodboard para Sometimes a Thousand Twangling instruments (SaTTi, a partir de ahora).
Picture
En la parte de Decentraland de SaTTi consiste en una serie de banners donde puedes cambiar los sonidos que se reproducen (los cuales se generan a partir de los dibujos), y eso cambiara la "música" dependiendo de donde pares los banners y donde estés, haciendo que el sonido se superponga de diferentes maneras y haciendo que los sonido provengan desde direcciones diferentes.
Picture
Tras ver el moodboard y sabiendo de que trataba el proyecto, y porque la fecha de entrega estaba muy cerca, decidí tomar el mismo enfoque que con las texturas: generadores de capas, alfas, y otros elementos que ya existían en Painter, para crear las texturas necesarias.
La Misión

Crear una gran variedad de texturas y materiales para hacer que la escena sea mas interesante de una manera rápida y segura, para que coincidiera con la apariencia del moodboard.
Recursos

  • Substance Painter 2018.2+
Los suelos

Picture
Esta fue la descripción y las referencias que se me dieron para producir algo similar pero que tuviera un aspecto más acuoso.
Este es el grupo de rellenos, filtros y generadores que use para crear el aspecto deseado. No compartiré los ajustes exactos porque el punto es llegar hasta la razón de porque los he usado.
​
  • Fill → Liquid
  • Blur Directional con baja intensidad
  • Blur Directional con menor intensidad pero con menor escala, para los detalles que aparecen con mayor frecuencia.
  • Warp para devolver a la textura un poco del efecto liquido pero con una dirección "principal" en esta ocasión.
  • Warp pero menor intensidad y escala
  • Levels para invertir los colores y ajustarlos con los blancos y negros.
  • Levels para hacer lo mismo que antes, pero esta vez lo ajustaremos para el roughness, así que las partes negras sean más brillantes y darle al material un aspecto más interesante cuando le de la luz.
Picture
Picture
Los Hexágonos

Picture
Picture
Estos son algunos de los ejemplos, del aspecto que queriamos para las rocas modificadas por el hombre. Realmente me gusto el contraste entre el pulido, el aspecto mecanizado con lo irregular y rugoso de las rocas naturales
Las pulidas

Decide trabajar estas texturas como como un grupo de diferentes capas, en lugar de usar múltiples filtros en una sola capa, así contaba con más flexibilidad para crear variaciones, y especialmente para controlar las diferentes opacidades de los canales en cada capa. Así estas son las capas con las opciones que use para esto:
​
  • Fill → Grunge Galvanic Large en el canal de Roughness, y con la opcion en la capa de Color Dodge
  • Duplica la capa, ponla debajo de la anterior y añade:
    • Blur Directional
  • Fill → Fluid con un filtro de Blur en canal de Height, para los arañazos de la superficie
Picture
Los orgánicos

De nuevo, ataje usando un grupo de capas. Estas son las capas y la opciones que use para esto:
​
  • Fill → Grunge Galvanic Large en el canal de Roughness para ganar un poco de variación en el spec
  • Fill → Crystal 1 en el canal de Height con Warp, Blur, y repite esas dos ultimas para darle a la superficie una aspecto más orgánico y poroso.
  • Fill → Crystal 1 con una gran escala en el canal de Height con Warp, Blur, y repite esas dos ultimas para darle a la superficie una aspecto más orgánico y poroso.
  • Fill → Crystal 1 en el canal de Height con un gran Warp, lo suficiente para hacer que parezcan grandes formas suaves en la superficie, entonces añade un Blur para suavizarlo.
  • Fill → Crystal 1 con una escala pequeña en el canal de Height con un gran Warp, lo suficiente para parecer pequeños pinchos salen de la superficie, añade luego un Blur para suavizarlo.
  • Fill → BnW Spots 1 con los settings que tiene por defecto (clic en el botón de seed para conseguir un patrón único) en el canal de la Height para darle un aspecto orgánico a la superficie
  • Duplicar la ultima capa, invierte los colores y con settings por defecto y reduce el Balance para conseguir una mascara ajustada, para las cavidades interiores
  • Usando BnW Spots 1 con una seed aleatoria, modificando el Colour, Height y Roughness para conseguir un mapa más detallado y mate
Picture
Conclusión

No todas las texturas hechas no fueron utilizadas al final, pero sirvieron para explorar la estética de la parcela y siempre podemos usarla en caso de querer actualizar la escena o desarrollar su aspecto visual más allá.
Picture
Obsidian
Picture
Carbon
Picture
Floors
Picture
Rocks
El hecho de que usáramos Painter para hacer las texturas significa que podemos exportar las texturas en 4K y cambiar el tamaño para que se ajuste a las limitaciones de Decentraland ( max Resolución 512x512 por textura). Cada una de las texturas tenia más de tres variaciones hechas, cambiando la opacidad, opciones de blending, generadores y efectos te permite generar variantes muy fácilmente cuando tienes prisa.

Picture
Álex
GENERALISTA 3D
Alejandro Bielsa is a junior 3D artist working at Polygonal Mind's in-house team. Passionate about videogames, vivid tutorial drinker and cat lover.
Twitter
0 Comments

Developing the aesthethic of SaTTi

5/4/2020

0 Comments

 
Español
Picture
With the release to the public of Decentraland, I was given a scene block out and I was tasked with the rearranging the elements around and making textures based on a moodboard for Sometimes a Thousand Twangling instruments (SaTTi, from now on).
Picture
The Decentraland part of SaTTi consists in a series of banners where you can change what sounds are playing on them (which are generated from drawings) and that change the "music" depending on where you stop the banners and where you stand in the floor, making the sounds overlap in different ways and making the sounds come from different directions.
After seeing the moodboard and knowing what the project was about, and because I was in a tight deadline, I decided to take the same approach for textures: layer generators, alphas, and other elements that already existed in Painter to create the needed textures.
Picture
The Mission

Creating a wide variety of textures and materials to make the scene more interesting in a fast and reliable way, to match a moodboard look.
Resources

  • Substance Painter 2018.2+
The Floors

Picture
This was the brief and references I was given to produce something similar but with a more liquid look to it.
This is the stack of fills, filters and generators I used to create the desired look. I won't share the exact settings of the effects because the point is to get straight the reason of why I used them
​
  • Fill → Liquid
  • Blur Directional with low intensity
  • Blur Directional with lower intensity but lower scale, for the high frequency details
  • Warp to bring back a bit of the liquid effect but with a "main" direction this time
  • Warp but with a smaller intensity and scale
  • Levels to invert the colours and adjust the clipping blacks and whites
  • Levels to do the same as before, but this time for the Roguhness, so the black parts are rougher and give the material a more interesting look when ligh hits them.
Picture
Picture
The Hexagons

Picture
Picture
These are some of the examples we were going for when making man-modified rocks. I really liked the contrast between the polished, machined aesthetics with the irregular and bumpiness of the natural ones.
The polished ones

I decided to approach these ones as a stack of different layers instead of using multiple fills and filters in a single layer, since it gave me more flexibility to create variations, specially controlling the different opacities of channels in each layer. So these are the layers and blending options I used for this:
​
  • Fill → Grunge Galvanic Large in the Roughness channel, and with a Color Dodge blend option
  • Duplicate the layer, put it under the previous one and add:
    • Blur Directional
  • Fill → Fluid with a Blur filter in the Height layer, for those surface scratches
Picture
The organic ones

Again, I approached these as a stack of layers These are the layers and blending options I used for this:
​
  • Fill → Grunge Galvanic Large on the Roughness channel for a bit of variation on the spec
  • Fill → Crystal 1 on the Height channel with Warp, Blur, and repeat those last two to give the surface a more organic and porous look
  • Fill → Crystal 1 with a bigger scale on the Height channel with Warp, Blur, and repeat those last two to give the surface a more organic and porous look
  • Fill → Crystal 1 on the Height channel with a big Warp, enough to make it look like big soft shapes wobbling the surface, then Blur to smooth it out
  • Fill → Crystal 1 with a small scale on the Height channel with a big Warp, enough to make it look like little spikes coming out, then Blur to smooth it out
  • Fill → BnW Spots 1 with default settings (click on the seed button to get a unique pattern) on the Height channel to give that organic surface look
  • Duplicate that last layer, invert the colours and with default settings and reduce the Balance to get a clipped mask, for the inside cavities
  • Using BnW Spots 1 with a new random seed, modifying the Colour, Height and Roughness to a more matte, high-detailed one
Picture
Conclusion

Not every texture made was used in the end, but it served to explore the aesthetic of the parcel, and we can always use them in case we want to update the scene or keep developing the visual aspect of it further.
Picture
Obsidian
Picture
Carbon
Picture
Floors
Picture
Rocks
The fact that we used Painter to make the textures means that we can export the textures in 4k and change the size after to meet the limitations of Decentraland (max resolution 512x512 per texture). Each of the textures here had 3 more variations made to them. Changing the opacity, blending options, seed of the generators and effects lets you generate variants very easily when you're in a rush.

Picture
Álex
3D GENERALIST
Alejandro Bielsa is a junior 3D artist working at Polygonal Mind's in-house team. Passionate about videogames, vivid tutorial drinker and cat lover.
Twitter
0 Comments

    Categories

    All
    Blender
    CryptoAvatars
    Decentraland
    Decentraland En Español
    Maya
    Metaverse
    Mixamo
    Morphite
    Substance Painter
    The Sandbox
    Totally Reliable Delivery Service
    Unity 3D
    Updates
    Vrchat

    Archives

    March 2022
    July 2021
    June 2021
    May 2021
    April 2021
    March 2021
    February 2021
    January 2021
    December 2020
    October 2020
    August 2020
    July 2020
    June 2020
    May 2020
    April 2020
    March 2020
    February 2020
    December 2019
    October 2019
    September 2019
    August 2019
    June 2019
    May 2019
    February 2019
    January 2019
    December 2018
    November 2018
    October 2018
    September 2016

    Picture
Home
Projects
Assets

Picture
Crypto
Friendly
Picture

Subscribe to get some 💚 in your inbox once in a while.

Follow us and your visit will never be forgotten!
Picture
Picture
Picture

 © 2015-2022 POLYGONAL MIND LTD. ALL RIGHTS RESERVED.
  • Home
  • Metaverse Builder
  • CryptoAvatars
  • MegaCube
  • Blog
  • Decentraland
  • Projects
  • Assets