Creating Portal Piston Panels: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
(Added brushwork for panel geometry)
(Added panel movement section)
Line 48: Line 48:


===Inner cylinders===
===Inner cylinders===
Every successive inner piston cylinder should be 8 units smaller in diameter than the previous one. Texture all successive cylinders with <code>metal/metal_lift001</code>. Note that the length of the piston cylinders should be less than the one before it. The size of these piston brushes need to add up to at least the target extension height of the piston. Once at the full height, resize the top cylinder to be 8 units below a major gridline (32, 64, 128 units), so you have room for the panel on top.
Every successive inner piston cylinder should be 8 units smaller in diameter than the previous one. Texture all successive cylinders with <code>metal/metal_lift001</code>. Note that the length of the piston cylinders should be less than the one before it. The size of these piston brushes need to add up to at least the target extension height of the piston. In this guide, the panel will have a target extension of 256 units. Once at the full height, resize the top cylinder to be 16 units below a major gridline (32, 64, 128 units), so you have room for the panel on top.


If you require the pistons to extend farther, you can make the largest piston cylinder wider and/or longer to accommodate. Note that you may need to increase the depth of the pit as well.
If you require the pistons to extend farther, you can make the largest piston cylinder wider and/or longer to accommodate. Note that you may need to increase the depth of the pit as well.
Line 70: Line 70:


=== Creating the panel ===
=== Creating the panel ===
For the panel, create a <code>128w*128l*8h</code> brush textured on the top with <code>concrete/concrete_modular_floor001a</code>, on the sides with <code>signage/hazard_orange_03b</code>, and on the bottom with <code>metal/citadel_metalwall060a</code>. Position it on top of your top piston cylinder.
For the panel, create a <code>128w*128l*8h</code> brush textured on the top with your floor or wall texture, on the sides with <code>signage/hazard_orange_03b</code>, and on the bottom with <code>metal/citadel_metalwall060a</code>. Position it on top of your top piston cylinder.
[[File:Piston panel on top.png|thumb|left|Panel textured and positioned atop the piston cylinders]]
[[File:Piston panel on top.png|thumb|left|Panel textured and positioned atop the piston cylinders]]
{{clr}}
{{clr}}
Line 79: Line 79:
{{clr}}
{{clr}}


Next, hide the panel create a <code>112w*112l*4h</code> brush inside the detail brush. Use <code>Tools > Carve</code> or <code>Ctrl+Shift+C</code> to carve that piece out of the detail brush. Then remove the temporary brush. Tie this, along with the panel surface, to a <code>func_detail</code>.
Next, use the clipping tool to carve out a <code>112w*112l</code> hole in the detail brush. The sides of the detail brush should be 8 units wide, as seen in the image below. Then, tie it, along with the panel surface itself, to a <code>func_brush</code>.
[[File:Panel details complete.png|thumb|left|Top panel details complete and textured]]
[[File:Panel details.png|thumb|left|Top panel details complete and textured]]
{{clr}}
 
==Making it Move==
If all you need is a static panel that doesn't move, you can simply stop here. If it doesn't move, tie all the panel pieces to <code>func_detail</code> entities instead of <code>func_brush</code>.
 
Making the panel move in and out uses a series of <code>func_door</code> entities. If you only have one moving piston cylinder, however, you can accomplish this using a <code>func_movelinear</code> entity instead. However, due to a bug with the <code>func_movelinear</code> entity, parenting doesn't work. This is why this tutorial is going to focus on the <code>func_door</code> implementation.
 
For the sake of this tutorial, the panel will move from fully recessed to 256 units extended.
 
=== Creating the <code>func_door</code> entities ===
For the process of creating the func_doors, it can get cramped with all the entities overlapping, so it is recommended to hide all the piston cylinders for now.
 
Since the target extension distance is 256 units, that means that each piston cylinder must move 128 units, since we have 2 inner cylinders.
 
For the first extension, create a <code>16w*16l*128h</code> brush textured with <code>nodraw</code>. Tie this to a <code>func_door</code>. Set the following property values:
 
{| class="standard-table sortable"
|-
! Property Name !! Value
|-
| Name || piston_cylinder1
|-
| Speed || 15
|-
| Delay Before Reset || -1
|-
| Move Direction || -90 0 0
|}
{{clr}}
 
Set the speed to the desired movement speed divided by the number of piston segments. For a speed of 30, the speed values in this case need to be 15.
 
Be sure to also uncheck the <code>Touch Opens</code> flag.
 
[[File:Piston cylinder1.png|thumb|left|Cylinder 1's func_door entity, centered in the hole]]
{{clr}}
 
For the second extension, duplicate this entity, and change the following property values:
 
{| class="standard-table sortable"
|-
! Property Name !! Value
|-
| Name || piston_cylinder2
|-
| Parent || piston_cylinder1
|}
{{clr}}
 
Repeat this for however many extensions you have. Note that the position and height of these <code>func_door</code> entities is not super important, however, if you want to make them different heights so they don't overlap, then simply change the <code>Lip</code> property value to be the entity height minus the desired extension distance. In this case, the height of the <code>func_door</code> entities are 32 units each, so the Lip value is 32 - 128 or -96.
[[File:Func door entities.png|thumb|left|The func_door entities]]
{{clr}}
 
=== Adding Moving Logic ===
This is where this tutorial can get a little bit difficult. This setup moves all the cylinders simultaneously, and is easier than the alternative of extending one after the other.
 
Firstly, create 2 <code>ambient_generic</code> entities. Set the property values to the following:
 
{| class="standard-table sortable"
|-
! Property Name !! Value
|-
| Name || piston_sound_start
|-
| Sound Name || apc_engine_start
|-
| SourceEntityName || piston_cylinder1
|}
{{clr}}
 
{| class="standard-table sortable"
|-
! Property Name !! Value
|-
| Name || piston_sound_stop
|-
| Sound Name || apc_engine_stop
|-
| SourceEntityName || piston_cylinder1
|}
{{clr}}
 
On the <code>piston_sound_start</code> entity, make sure to uncheck the <code>Is NOT looped</code> flag.
 
Next, create a <code>math_counter</code> entity, and set the following property values:
 
{| class="standard-table sortable"
|-
! Property Name !! Value
|-
| Name || piston_counter
|-
| Maximum Legal Value || 2
|}
{{clr}}
 
On the <code>math_counter</code> entity, set the following outputs:
 
{| {{OutputsTable}}
| [[File:Io21.png]] || OnHitMax || piston_sound_start || StopSound || <none> || 0.00 || No
|-
| [[File:Io21.png]] || OnHitMax || piston_sound_stop || PlaySound || <none> || 0.00 || No
|-
| [[File:Io21.png]] || OnHitMin || piston_sound_start || StopSound || <none> || 0.00 || No
|-
| [[File:Io21.png]] || OnHitMin || piston_sound_stop || PlaySound || <none> || 0.00 || No
|}
{{clr}}
 
Now, create a <code>logic_relay</code> entity. Name it <code>piston_extend</code> and give it the following outputs:
 
{| {{OutputsTable}}
| [[File:Io21.png]] || OnTrigger || piston_cylinder1 || Open || <none> || 0.00 || No
|-
| [[File:Io21.png]] || OnTrigger || piston_cylinder2 || Open || <none> || 0.00 || No
|-
| [[File:Io21.png]] || OnTrigger || piston_sound_start || PlaySound || <none> || 0.00 || No
|}
{{clr}}
 
Similarly, create another <code>logic_relay</code> entity. Name it <code>piston_retract</code> and give it the following outputs:
 
{| {{OutputsTable}}
| [[File:Io21.png]] || OnTrigger || piston_cylinder1 || Close || <none> || 0.00 || No
|-
| [[File:Io21.png]] || OnTrigger || piston_cylinder2 || Close || <none> || 0.00 || No
|-
| [[File:Io21.png]] || OnTrigger || piston_sound_start || PlaySound || <none> || 0.00 || No
|}
{{clr}}
 
Lastly, on each of the <code>func_door</code> entities, set the following outputs:
 
{| {{OutputsTable}}
| [[File:Io21.png]] || OnFullyOpen || piston_counter || Add || 1 || 0.00 || No
|-
| [[File:Io21.png]] || OnFullyClosed || piston_counter || Subtract || 1 || 0.00 || No
|}
{{clr}}
 
=== Parenting brushes ===
Now we need to parent the piston brushes we created earlier to the<code>func_door</code> entities.
 
First, move the piston brushes into place in a retracted state.
[[File:Piston retracted.png|thumb|left|The piston, retracted]]
{{clr}}
 
Next, select each piston cylinder and parent it to its respective <code>func_door</code> entity. Parent the piston panel to the last cylinder door.
 
== Usage ==
Simply send a <code>Trigger</code> input to the <code>piston_extend</code> and <code>piston_retract</code> to extend and retract the piston, respectively.
 
[[File:Panel retracted ingame.png|thumb|left|Panel retracted ingame]]
[[File:Panel extended ingame.png|thumb|left|Panel extended ingame]]
{{clr}}
{{clr}}



Revision as of 12:36, 22 March 2024

Portal Level Creation

Introduction

This guide will show you how to create Piston Panels. These are one of the most seen objects in Portal.

Warning.pngWarning: This guide is incomplete. As of right now, it is recommended that you don't follow this guide.

The Hole

First, make sure you have a hole the area you want to put a piston in. The hole should be at least 64 and 128 units long in width, length or height. Texture the sides of the hole with metal/metalwall061f.

Piston Hole

Piston Pit

Orange brush

Create a 128w*128w*16h with the nodraw texture. Texture the visible face with lights/light_orange001

Orange Brush

Piston Pit Walls

Create two 16w*128l*128h brushes and 128w*16l*128h brushes with the nodraw texture.

Change the texture of all visible faces to metal/metalwall_bts_006b_gradient. Use the Face Edit Sheet dialog and set its rotation to 180, realign the texture if necessary.

Create two 16w*128l*112h brushes and 128w*16l*112h brushes with the nodraw texture.

Change the texture of all visible faces to metal/metalwall_bts_006b and put these brushes on top of the other 2 brushes.

Wall brushes

Piston Pit Grate

Create 2 32w*128l*1h brushes and 2 64w*32l*1h brushes with the nodraw texture. Set the front face texture to /metal/metalgrate018.

Create a 64w*64l*1h brush the nodraw texture. Set the front face texture to metal/metalgrate018c.

Position these brushes right below the beginning of the brushes that create the hole entrance. Remember to tie these to a func_detail.

Grates

The Piston Brushes

Base brush

First, create a 12-sided cylinder with a size of 32w*32l*192h. Texture the sides with metal/metal_lift001 and metal/metal_lift001_gradient, and the top and bottom with nodraw.

Textured piston base with pit hidden

Inner cylinders

Every successive inner piston cylinder should be 8 units smaller in diameter than the previous one. Texture all successive cylinders with metal/metal_lift001. Note that the length of the piston cylinders should be less than the one before it. The size of these piston brushes need to add up to at least the target extension height of the piston. In this guide, the panel will have a target extension of 256 units. Once at the full height, resize the top cylinder to be 16 units below a major gridline (32, 64, 128 units), so you have room for the panel on top.

If you require the pistons to extend farther, you can make the largest piston cylinder wider and/or longer to accommodate. Note that you may need to increase the depth of the pit as well.

Extended piston with 2 additional cylinders, each with a height of 192 units.

Joint pieces

To create the joints between pistons, create a 32w*32l*8h 12-sided cylinder brush. Texture the sides with metal/metalwall_bts_005a. Position it directly on top of the base brush.

Next, use the vertex tool to move the top vertices to the next smallest piston size. Select the vertices in the 3D view before moving them.

Piston joint textured and positioned above base brush

Repeat this process for all the smaller pistons, except the last one. For the last one, simply duplicate the last one and flip it over. Position it at the top of the cylinder.

Joints on all piston pieces

Finally, for each piston piece, select the brush and the joint above it, and tie them to a func_brush. For the bottom one, as it is not going to move, you can simply tie it to a func_detail.

Piston brushes tied to entities

Creating the panel

For the panel, create a 128w*128l*8h brush textured on the top with your floor or wall texture, on the sides with signage/hazard_orange_03b, and on the bottom with metal/citadel_metalwall060a. Position it on top of your top piston cylinder.

Panel textured and positioned atop the piston cylinders

Panel Details

The panel details can be created by creating a 128w*128l*4h brush textured with plastic/plasticwall001b. Use the clipping tool to remove the corners on the bottom (as seen in the image below)

Panel detail brush with bottom corners removed

Next, use the clipping tool to carve out a 112w*112l hole in the detail brush. The sides of the detail brush should be 8 units wide, as seen in the image below. Then, tie it, along with the panel surface itself, to a func_brush.

Top panel details complete and textured

Making it Move

If all you need is a static panel that doesn't move, you can simply stop here. If it doesn't move, tie all the panel pieces to func_detail entities instead of func_brush.

Making the panel move in and out uses a series of func_door entities. If you only have one moving piston cylinder, however, you can accomplish this using a func_movelinear entity instead. However, due to a bug with the func_movelinear entity, parenting doesn't work. This is why this tutorial is going to focus on the func_door implementation.

For the sake of this tutorial, the panel will move from fully recessed to 256 units extended.

Creating the func_door entities

For the process of creating the func_doors, it can get cramped with all the entities overlapping, so it is recommended to hide all the piston cylinders for now.

Since the target extension distance is 256 units, that means that each piston cylinder must move 128 units, since we have 2 inner cylinders.

For the first extension, create a 16w*16l*128h brush textured with nodraw. Tie this to a func_door. Set the following property values:

Property Name Value
Name piston_cylinder1
Speed 15
Delay Before Reset -1
Move Direction -90 0 0

Set the speed to the desired movement speed divided by the number of piston segments. For a speed of 30, the speed values in this case need to be 15.

Be sure to also uncheck the Touch Opens flag.

Cylinder 1's func_door entity, centered in the hole

For the second extension, duplicate this entity, and change the following property values:

Property Name Value
Name piston_cylinder2
Parent piston_cylinder1

Repeat this for however many extensions you have. Note that the position and height of these func_door entities is not super important, however, if you want to make them different heights so they don't overlap, then simply change the Lip property value to be the entity height minus the desired extension distance. In this case, the height of the func_door entities are 32 units each, so the Lip value is 32 - 128 or -96.

The func_door entities

Adding Moving Logic

This is where this tutorial can get a little bit difficult. This setup moves all the cylinders simultaneously, and is easier than the alternative of extending one after the other.

Firstly, create 2 ambient_generic entities. Set the property values to the following:

Property Name Value
Name piston_sound_start
Sound Name apc_engine_start
SourceEntityName piston_cylinder1
Property Name Value
Name piston_sound_stop
Sound Name apc_engine_stop
SourceEntityName piston_cylinder1

On the piston_sound_start entity, make sure to uncheck the Is NOT looped flag.

Next, create a math_counter entity, and set the following property values:

Property Name Value
Name piston_counter
Maximum Legal Value 2

On the math_counter entity, set the following outputs:

  My Output Target Entity Target Input Parameter Delay Only Once
Io21.png OnHitMax piston_sound_start StopSound <none> 0.00 No
Io21.png OnHitMax piston_sound_stop PlaySound <none> 0.00 No
Io21.png OnHitMin piston_sound_start StopSound <none> 0.00 No
Io21.png OnHitMin piston_sound_stop PlaySound <none> 0.00 No

Now, create a logic_relay entity. Name it piston_extend and give it the following outputs:

  My Output Target Entity Target Input Parameter Delay Only Once
Io21.png OnTrigger piston_cylinder1 Open <none> 0.00 No
Io21.png OnTrigger piston_cylinder2 Open <none> 0.00 No
Io21.png OnTrigger piston_sound_start PlaySound <none> 0.00 No

Similarly, create another logic_relay entity. Name it piston_retract and give it the following outputs:

  My Output Target Entity Target Input Parameter Delay Only Once
Io21.png OnTrigger piston_cylinder1 Close <none> 0.00 No
Io21.png OnTrigger piston_cylinder2 Close <none> 0.00 No
Io21.png OnTrigger piston_sound_start PlaySound <none> 0.00 No

Lastly, on each of the func_door entities, set the following outputs:

  My Output Target Entity Target Input Parameter Delay Only Once
Io21.png OnFullyOpen piston_counter Add 1 0.00 No
Io21.png OnFullyClosed piston_counter Subtract 1 0.00 No

Parenting brushes

Now we need to parent the piston brushes we created earlier to thefunc_door entities.

First, move the piston brushes into place in a retracted state.

The piston, retracted

Next, select each piston cylinder and parent it to its respective func_door entity. Parent the piston panel to the last cylinder door.

Usage

Simply send a Trigger input to the piston_extend and piston_retract to extend and retract the piston, respectively.

Panel retracted ingame
Panel extended ingame

See also